[NOIP2015总结]

来源:互联网 发布:tcpdump 指定端口抓包 编辑:程序博客网 时间:2024/05/16 08:48

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 1000000007const 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 0x7fffffffconst 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
原创粉丝点击