关闭

[NOIP2015总结]

268人阅读 评论(0) 收藏 举报

Day1:

T1:

直接模拟就行。

#include<iostream>  
#include<cstdio>  
#include<cstring>  
using namespace std;  
const int N=100;  
int n,ans[N][N];  
struct S{int x,y;}a[N*N];  
int main(){
    int i,j,x,y;  
    scanf("%d",&n);  
    ans[1][n/2+1]=1;  
    a[1].x=1;a[1].y=n/2+1;  
    for(i=2;i<=n*n;++i){  
        x=a[i-1].x;y=a[i-1].y;  
        if(x==1&&y!=n){  
            ans[n][y+1]=i;  
            a[i].x=n;a[i].y=y+1;  
        }  
        else if(x!=1&&y==n){  
            ans[x-1][1]=i;  
            a[i].x=x-1;a[i].y=1;  
        }  
        else if(x==1&&y==n){  
            ans[x+1][y]=i;  
            a[i].x=x+1;a[i].y=y;  
        }  
        else{  
            if(x-1>0&&y+1<=n&&!ans[x-1][y+1]){  
                ans[x-1][y+1]=i;  
                a[i].x=x-1;a[i].y=y+1;  
            }  
            else{  
                ans[x+1][y]=i;  
                a[i].x=x+1;a[i].y=y;  
            }  
        }  
    }  
    for(i=1;i<=n;++i){  
        for(j=1;j<=n;++j)  
          printf("%d ",ans[i][j]);  
        printf("\n");  
    }  
    return 0;  
}  

T2:

题目是让求出一个最小环,因为是有向图而且每个点只有一条出边,所以直接dfs就行。

#include<iostream>  
#include<cstdio>  
#include<cstring>  
using namespace std;  
const int N=200010;  
bool check;  
struct S{int st,en;}aa[N*10];  
int n,a[N],tot,ans,point[N],next[N*10],f[N],use[N];  
inline int in(){  
    int x=0;char ch=getchar();  
    while(ch<'0'||ch>'9') ch=getchar();  
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();  
    return x;  
}  
inline void add(int x,int y){  
    tot+=1;next[tot]=point[x];point[x]=tot;  
    aa[tot].st=x;aa[tot].en=y;  
}  
inline void find(int x,int y,int z){  
    int i;  
    if(check) return ;  
    f[x]=y;use[x]=z;  
    for(i=point[x];i;i=next[i]){  
        if(use[aa[i].en]==0) find(aa[i].en,y+1,z);  
        else if(use[aa[i].en]==z){  
            ans=min(ans,y-f[aa[i].en]+1);  
            check=true;  
            return ;  
        }  
    }  
}  
int main(){
    int i,num=0;  
    n=in();  
    for(i=1;i<=n;++i) a[i]=in(),add(i,a[i]);  
    ans=0x7fffffff;  
    for(i=1;i<=n;++i)  
      if(!use[i]){  
        num+=1;  
        check=false;  
        find(i,1,num);  
      }  
    printf("%d\n",ans);  
    return 0;  
}  

T3:

刚看到题的时候感觉像是一个最短路??状压??
但是感觉应该写不完,所以直接写的暴力。
后来听了TA爷说的,可以给操作编上号以后按照顺序直接dfs就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int T,n,a[30],b[30],ans;
//1:三顺子  2:双顺子  3:单顺子   4:三带二,一  6:四带二 
inline bool check(){
    for(int i=1;i<=15;++i)
      if(a[i]) return false;
    return true;
}
inline void dfs(int x,int y){
    int i,j,k,kind,sum=0;
    if(y>=ans) return ;
    if(check()){
        ans=min(ans,y);
        return ;
    }
    for(i=1;i<=13;++i) sum+=a[i]?1:0;
    sum+=a[14]+a[15]?1:0; ans=min(ans,y+sum);
    for(kind=x;kind<=5;++kind){
        if(kind==1){
            for(i=1;i<=11;++i)
              if(a[i]>=3)
                for(j=i+1;j<=12&&a[j]>=3;++j){
                    for(k=i;k<=j;++k) a[k]-=3;
                    dfs(x,y+1);
                    for(k=i;k<=j;++k) a[k]+=3;
                }
        }
        if(kind==2){
            for(i=1;i<=10;++i)
              if(a[i]>=2&&a[i+1]>=2)
                for(j=i+2;j<=12&&a[j]>=2;++j){
                    for(k=i;k<=j;++k) a[k]-=2;
                    dfs(x,y+1);
                    for(k=i;k<=j;++k) a[k]+=2;
                }
        }
        if(kind==3){
            for(i=1;i<=8;++i){
                sum=0;
                for(j=i;j<=i+3;++j)
                  if(a[j]) sum+=1;
                if(sum!=4) continue;
                for(j=i+4;j<=12&&a[j];++j){
                    for(k=i;k<=j;++k) a[k]-=1;
                    dfs(x,y+1);
                    for(k=i;k<=j;++k) a[k]+=1;
                }
            }
        }
        if(kind==4){
            for(i=1;i<=15;++i)
              if(a[i]>=3){
                a[i]-=3;
                for(j=1;j<=15;++j)
                  if(a[j]>=2)
                    a[j]-=2,dfs(x,y+1),a[j]+=2;
                a[i]+=3;
              }
            for(i=1;i<=15;++i)
              if(a[i]>=3){
                a[i]-=3;
                for(j=1;j<=15;++j)
                  if(a[j])
                    a[j]-=1,dfs(x,y+1),a[j]+=1;
                a[i]+=3;
              }
        }
        if(kind==5){
            for(i=1;i<=15;++i)
             if(a[i]>=4){
                a[i]-=4;
                for(j=1;j<=15;++j)
                  if(a[j]>=2){
                    a[j]-=2;
                    for(k=j;k<=15;++k)
                      if(a[k]>=2)
                        a[k]-=2,dfs(x,y+1),a[k]+=2;
                    a[j]+=2;
                  }
                a[i]+=4;
             }
            for(i=1;i<=15;++i)
             if(a[i]>=4){
                a[i]-=4;
                for(j=1;j<=15;++j)
                  if(a[j]){
                    a[j]-=1;
                    for(k=j;k<=15;++k)
                      if(a[k])
                        a[k]-=1,dfs(x,y+1),a[k]+=1;
                    a[j]+=1;
                  }
                a[i]+=4;
             }
        }
    }
}
int main(){
    scanf("%d%d",&T,&n);
    while(T--){
        int i,j,x,y;
        memset(a,0,sizeof(a));
        for(i=1;i<=n;++i){
            scanf("%d%d",&x,&y);
            if(!x) a[y+13]+=1;
            if(x>=3) a[x-2]+=1;
            if(x>0&&x<3) a[x+11]+=1;
        }
        ans=0;
        for(i=1;i<=13;++i) ans+=a[i]?1:0;
        ans+=a[14]+a[15]?1:0;
        dfs(1,0);
        printf("%d\n",ans);
    }
}

Day2

T1:

二分答案+贪心验证。贪心的时候直接扫一遍就行了。

#include<iostream>  
#include<cstdio>  
#include<cstring>  
using namespace std;  
#define mid (l+r)/2  
const int N=50010;  
int L,n,m,a[N];  
inline bool check(int x){  
    int now=0,sum=0,i;  
    for(i=2;i<=n+2;++i){  
        if(a[i]-now>=x){  
            now=a[i];  
        }  
        else{  
            sum+=1;  
        }  
    }  
    return sum<=m;  
}  
int main(){ 
    int i,j;  
    scanf("%d%d%d",&L,&n,&m);  
    a[n+2]=L;  
    for(i=1;i<=n;++i) scanf("%d",&a[i+1]);  
    int l=0,r=L,ans=0;  
    while(l<=r){  
        if(check(mid)) ans=max(ans,mid),l=mid+1;  
        else r=mid-1;  
    }  
    printf("%d\n",ans);  
    return 0;  
}  

T2:

首先70分的dp很好写,就是f[i][j][k]表示到第一个的i,第二个的j,分成了k段的答案。那么首先处理出到i,j时连续的相同的长度,就比较好转移了。
那么满分的就是将转移的时候的枚举改成前缀和就好了。注意这个题还卡内存,所以需要将k的那一维滚了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define D 1000000007
const int M=210;
const int N=1010;
char s1[N],s2[M];
int n,m,k,f[2][N][M],sum[N][M];
inline char in(){
    char ch=getchar();
    while(ch<'a'|ch>'z') ch=getchar();
    return ch;
}
int main(){
    int i,j,p,now=0;
    scanf("%d%d%d",&n,&m,&k);
    sum[0][0]=1;
    for(i=1;i<=n;++i) s1[i]=in(),sum[i][0]=1;
    for(i=1;i<=m;++i) s2[i]=in();
    for(i=1;i<=n;++i)
      for(j=1;j<=m;++j)
        sum[i][j]=(s1[i]==s2[j])?sum[i-1][j-1]:0;
    while(k--){
        now^=1;
        memset(f[now],0,sizeof(f[now]));
        for(i=1;i<=n;++i)
          for(j=1;j<=min(m,i);++j)
            f[now][i][j]=(f[now][i-1][j]+(s1[i]==s2[j]?sum[i][j]:0))%D;
        for(i=0;i<=n;++i) sum[i][0]=0;
        for(i=0;i<=m;++i) sum[0][i]=0;
        for(i=1;i<=n;++i)
          for(j=1;j<=m;++j)
            sum[i][j]=(s1[i]==s2[j])?((sum[i-1][j-1]+f[now][i-1][j-1])%D):0;
    }
    printf("%d\n",f[now][n][m]);
}

T3:

又是二分答案,主要的问题是在二分之后我们怎样去判断呢?
对于一个二分的答案mid,我们首先找出所有总长度大于mid的路线,然后我们求出这些路线的交,再找出交中最大的边减去,看看符不符合。
对于上面说的我们可以先处理处经过每条边的路线的数目,怎样统计呢?
我们对于一条边x,y,和他们的lca,在节点x和y处+1,在lca处-2,这样我们遍历一遍所有的节点,统计每一个父亲处的权值和后,就可以算出这个父亲的上一条边有多少路径经过了。
但是这种做法会背卡5分的常数,UOJ上好像有线性的做法,有时间的时候写一写吧。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define mid (l+r)/2
#define inf 0x7fffffff
const int N=300010;
struct Q{int x,y,lca,dis;}q[N];
struct S{int st,en,va;}aa[N*2];
int n,m,tot,point[N],next[N*2],fa[N][20],deep[N],dis[N],maxlen,maxn,v[N];
inline int in(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
inline void add(int x,int y,int z){
    tot+=1;next[tot]=point[x];point[x]=tot;
    aa[tot].st=x;aa[tot].en=y;aa[tot].va=z;
    tot+=1;next[tot]=point[y];point[y]=tot;
    aa[tot].st=y;aa[tot].en=x;aa[tot].va=z;
}
inline void prepare(int x,int last){
    int i,j;
    for(i=1;i<=19&&deep[x]>=(1<<i);++i)
      fa[x][i]=fa[fa[x][i-1]][i-1];
    for(i=point[x];i;i=next[i])
      if(aa[i].en!=last){
        deep[aa[i].en]=deep[x]+1;
        fa[aa[i].en][0]=x;
        dis[aa[i].en]=dis[x]+aa[i].va;
        prepare(aa[i].en,x);
      }
}
inline int LCA(int x,int y){
    if(deep[x]<deep[y]) swap(x,y);
    int t=deep[x]-deep[y],i;
    for(i=0;i<=19;++i)
      if(t&(1<<i)) x=fa[x][i];
    for(i=19;~i;--i)
      if(fa[x][i]!=fa[y][i])
        x=fa[x][i],y=fa[y][i];
    return x==y?x:fa[x][0];
}
inline int work(int x,int last){
    int i,sum=v[x];
    for(i=point[x];i;i=next[i])
      if(aa[i].en!=last) sum+=work(aa[i].en,x);
    if(sum==tot) maxn=max(maxn,dis[x]-dis[fa[x][0]]);
    return sum;
}
int main(){
    int i,j,x,y,z;
    n=in();m=in();
    for(i=1;i<n;++i){
        x=in();y=in();z=in();
        add(x,y,z);
    }
    prepare(1,0);
    for(i=1;i<=m;++i){
        q[i].x=in();q[i].y=in();
        q[i].lca=LCA(q[i].x,q[i].y);
        q[i].dis=dis[q[i].x]+dis[q[i].y]-2*dis[q[i].lca];
        maxlen=max(maxlen,q[i].dis);
    }
    int l=0,r=maxlen;
    while(l<r){
        tot=maxn=0;
        memset(v,0,sizeof(v));
        for(i=1;i<=m;++i)
          if(q[i].dis>mid)
            tot+=1,v[q[i].x]+=1,v[q[i].y]+=1,v[q[i].lca]-=2;
        work(1,0);
        if(maxlen-maxn<=mid) r=mid;
        else l=mid+1;
    }
    printf("%d\n",l);
}
1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:87517次
    • 积分:2475
    • 等级:
    • 排名:第14926名
    • 原创:149篇
    • 转载:0篇
    • 译文:0篇
    • 评论:24条