zoukankan      html  css  js  c++  java
  • NOIP2018TG题解

    应该不会被禁赛吧qwq

    Day1

    T1 铺设道路

    €€£:我 抄 我 自 己

    我考场上写的是一个类似于差分的做法,就是开个数组表示某个位置是否贡献答案,从小到大枚举高度,那么一个位置能贡献答案当且仅当这个位置没有被当前高度盖过去,并且上一个位置被当前高度盖过去了,如果某一时刻某个位置被当前高度盖过去了,那么如果这个位置有贡献,就要把这个位置贡献的扣掉,同时看一下能否使后一个位置产生贡献

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    #include<map>
    #define il inline
    #define db double
    #define LL long long
    
    using namespace std;
    const int N=100000+10;
    il LL rd()
    {
      LL x=0,w=1;char ch=0;
      while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
      while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
      return x*w;
    }
    struct node
    {
      int x,d;
      node(){}
      node(int nx,int nd){x=nx,d=nd;}
      bool operator < (const node &bb) const {return d<bb.d;}
    }b[N];
    int n,a[N];
    LL ans;
    int s[N],nw;
    
    int main()
    {
      //freopen("road.in","r",stdin);
      //freopen("road.out","w",stdout);
      n=rd();
      for(int i=1;i<=n;i++) a[i]=rd(),b[i]=node(i,a[i]);
      a[0]=a[n+1]=-1,b[0]=node(0,-1);
      sort(b,b+n+1);
      b[n+1].d=2333333;
      s[1]=nw=1;
      for(int h=0,i=1;h<=10000;h++)
        {
          if(!nw) break;
          if(h) ans+=nw;
          while(b[i].d==h)
    		{
    		  int x=b[i].x;
    		  if(s[x]) --s[x],--nw;
    		  if(a[x+1]>h) ++s[x+1],++nw; 
    		  ++i;
    		  //printf("%d %d
    ",h,nw);
    		} 
        }
      printf("%lld
    ",ans);
      return 0;
    }
    
    

    T2 货币系统

    这题的答案就是所有不能被其他数加起来表示的数的个数.原因是首先最小的那个数要选,然后这个数的倍数就可以删掉不选了.接下来没被删掉的数中最小的数也要选,同样可以把其他能被加起来表示的数删掉,,,以此类推.所以这题就是个完全背包,只需要知道给出的数中的每个数是否只有一种表示方法就好了

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    #include<map>
    #define il inline
    #define db double
    #define LL long long
    
    using namespace std;
    const int N=100+10,M=25000+10;
    il LL rd()
    {
      LL x=0,w=1;char ch=0;
      while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
      while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
      return x*w;
    }
    int n,m,a[N],f[M];
    
    int main()
    {
      //freopen("money.in","r",stdin);
      //freopen("money.out","w",stdout);
      int T=rd();
      while(T--)
        {
          memset(f,0,sizeof(f));
          n=rd(),m=0;
          for(int i=1;i<=n;i++) m=max(m,a[i]=rd());
          f[0]=1;
          for(int i=1;i<=m;i++)
    		{
    		  for(int j=1;j<=n;j++)
    			if(i-a[j]>=0) f[i]+=f[i-a[j]];
    		  f[i]=min(f[i],2);
    		}
          int ans=0;
          for(int i=1;i<=n;i++) ans+=(f[a[i]]==1);
          printf("%d
    ",ans);
        }
      return 0;
    }
    
    

    T3 赛道修建

    首先如果某个答案可行,那么比这个答案更小的也可行,所以可以二分答案.check的话就需要找出(m)条长度(ge mid)的链.可以对整棵树进行Dfs.每个点的儿子都有往下的链,我们就把这些链取出来统计一下,方法是用当前最短的链匹配加起来长度(ge mid)的,同时尽量小的链,然后计入链的个数,或者是本来长度就合法的可以直接计入链的个数,最后没匹配上的不能计入答案的就放到父亲处匹配

    实现可以sort之后使用二分+并查集,我比较懒就用了(multiset) 依旧能艹过去

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define il inline
    #define db double
    #define LL long long
    #define re register
    
    using namespace std;
    const int N=50000+10;
    il LL rd()
    {
      LL x=0,w=1;char ch=0;
      while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
      while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
      return x*w;#
    }
    int to[N<<1],nt[N<<1],w[N<<1],hd[N],tot=1;
    il void add(int x,int y,int z)
    {
      ++tot,to[tot]=y,nt[tot]=hd[x],w[tot]=z,hd[x]=tot;
      ++tot,to[tot]=x,nt[tot]=hd[y],w[tot]=z,hd[y]=tot;
    }
    int n,m,an,mid;
    int f[N],tp;
    multiset<int> s;
    multiset<int>::iterator it,rbq;
    void dfs(int x,int ffa)
    {
      f[x]=0;
      for(int i=hd[x];i;i=nt[i])
        {
          int y=to[i];
          if(y==ffa) continue;
          dfs(y,x);
        }
      s.clear(),tp=0;
      for(int i=hd[x];i;i=nt[i])
        {
          int y=to[i];
          if(y==ffa) continue;
          s.insert(f[y]+w[i]);
          ++tp;
        }
      it=s.begin();
      for(int i=2;i<=tp;i++) ++it;
      while(tp&&(int)(*it)>=mid)
        {
          rbq=it;
          ++an;
          --it,--tp;
          s.erase(rbq);
        }
      int ma=*it;
      it=s.begin();
      while(tp>1&&(int)(*it)+ma<mid) f[x]=max(f[x],(int)(*it)),--tp,rbq=it,++it,s.erase(rbq);
      //printf("--%d
    ",tp);
      //it=s.begin();
      while(tp>1)
        {
          rbq=s.lower_bound(mid-(*it));
          if(rbq==s.end()) f[x]=max(f[x],(int)(*it)),--tp,rbq=it,++it,s.erase(rbq);
          else
    		{
    		  ++an;
    		  if(rbq==it) ++rbq;
    		  --tp,s.erase(rbq);
    		  //printf("%d
    ",(int)(*rbq));
    		  --tp,rbq=it,++it,s.erase(rbq);
    		  //printf("qqc
    ");
    		}
        }
      if(tp) f[x]=max(f[x],*s.begin());
      //printf("%d %d
    ",x,f[x]);
    }
    il bool check()
    {
      an=0;
      dfs(1,0);
      return an>=m;
    }
    int zz=0,de[N];
    void dd(int x,int ffa)
    {
      for(int i=hd[x];i;i=nt[i])
        {
          int y=to[i];
          if(y==ffa) continue;
          de[y]=de[x]+w[i],dd(y,x);
        }
      if(de[x]>de[zz]) zz=x;
    }
    
    int main()
    {
      //freopen("track.in","r",stdin);
      //freopen("track.out","w",stdout);
      n=rd(),m=rd();
      int l=0,r=0;
      for(re int i=1;i<n;++i)
        {
          int x=rd(),y=rd(),z=rd();
          add(x,y,z);
        }
      de[0]=-1,de[1]=0,dd(1,0);
      de[r=zz]=0,zz=0,dd(r,0),r=de[zz];
      int ans=0;
      while(l<=r)
        {
          mid=(l+r)>>1;
          if(check()) ans=mid,l=mid+1;
          else r=mid-1;
        }
      printf("%d
    ",ans);
      return 0;
    }
    
    

    Day2

    T1 旅行

    首先如果是棵树,那么从1开始,贪心从小往大遍历所有儿子,答案就是按从前往后遍历到的点的序列.如果是基环树,那么枚举环上的边,断掉,然后当成树做,最后取字典序最小的答案就好了

    结果做这个##题花我1.5h qwq

    不过这题有线性做法,自己可以去找

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    #include<map>
    #define il inline
    #define LL long long
    #define db double
    #define re register
    
    using namespace std;
    const int N=5000+10;
    il LL rd()
    {
      LL x=0,w=1;char ch=0;
      while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
      while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
      return x*w;
    }
    int to[N<<1],nt[N<<1],hd[N],tot=1;
    il void add(int x,int y)
    {
      ++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;
      ++tot,to[tot]=x,nt[tot]=hd[y],hd[y]=tot;
    }
    struct edgee
    {
      int x,y;
      edgee(){}
      edgee(int nx,int ny){x=nx,y=ny;if(x>y) swap(x,y);}
      bool operator < (const edgee &bb) const {return x!=bb.x?x>bb.x:y>bb.y;}
    }e[N];
    int n,m,an[N],ans[N],tt;
    int st[N],s2[N],tp,se[N],te,zhushu;
    bool ban[N<<1],v[N];
    void dfs(int x)
    {
      v[x]=true,st[++tp]=x;
      for(int i=hd[x];i;i=nt[i])
    	{
    	  if(ban[i]) continue;
    	  s2[tp]=i;
    	  ban[i]=ban[i^1]=true;
    	  if(v[to[i]])
    		{
    		  int ttp=tp;
    		  while(ttp)
    			{
    			  int y=st[ttp],ii=s2[ttp--];
    			  se[++te]=ii;
    			  if(to[i]==y) break;
    			}
    		  zhushu=i;
    		  continue;
    		}
    	  else dfs(to[i]);
    	  ban[i]=ban[i^1]=false;
    	}
      st[tp]=s2[tp]=0,--tp;
    }
    void dd(int x)
    {
      an[++tt]=x;
      for(int i=hd[x];i;i=nt[i])
    	{
    	  if(ban[i]) continue;
    	  ban[i]=ban[i^1]=true;
    	  dd(to[i]);
    	  ban[i]=ban[i^1]=false;
    	}
    }
    
    int main()
    {
      //freopen("travel.in","r",stdin);
      //freopen("travel.out","w",stdout);
      n=rd(),m=rd();
      for(int i=1;i<=m;i++) e[i]=edgee(rd(),rd());
      sort(e+1,e+m+1);
      for(int i=1;i<=m;i++) add(e[i].x,e[i].y);
      if(m==n-1)
    	{
    	  dd(1);
    	  for(int i=1;i<=n;i++) printf("%d ",an[i]);
    	}
      else
    	{
    	  dfs(1);
    	  ban[zhushu]=ban[zhushu^1]=false;
    	  memset(ans,63,sizeof(ans));
    	  while(te)
    		{
    		  int i=se[te--];
    		  ban[i]=ban[i^1]=true;
    		  tt=0,dd(1);
    		  for(int j=1;j<=n;j++)
    			{
    			  if(an[j]>ans[j]) break;
    			  else if(an[j]<ans[j])
    				{
    				  for(int k=1;k<=n;k++) ans[k]=an[k];
    				  break;
    				}
    			}
    		  ban[i]=ban[i^1]=false;
    		}
    	  for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    	}
      return 0;
    }
    
    

    T2 填数游戏

    骗分过样例,暴力出奇迹

    下面是考场10'代码

    ......
    int n,m,ans;
    int a[5][5],tt;
    string s[20];
    void dd(int x,int y,string nw)
    {
      if(x==n&&y==m) {s[++tt]=nw;return;}
      if(x!=n) dd(x+1,y,nw+(char)(48+a[x+1][y]));
    }
    il bool check()
    {
      tt=0;
      //没写完好气哦
    }
    void dfs(int x,int y)
    {
      if(x>n) {ans+=check();return;}
      a[x][y]=0,dfs(y==m?x+1:x,y==m?1:y+1);
      a[x][y]=1,dfs(y==m?x+1:x,y==m?1:y+1);
    }
    
    int main()
    {
      //freopen("game.in","r",stdin);
      //freopen("game.out","w",stdout);
      n=rd(),m=rd();
      /*......*/
      if(n==2&&m==2) printf("12");
      if(n==3&&m==3) printf("112");
      return 0;
    }
    

    这题的打表题解自己去网上找qwq

    下面是靠谱的数学做法
    咕咕咕咕咕咕咕

    T3 保卫王国

    不会打暴力,身败名裂

    做法之一是动态dp,这里不做过多赘述

    还有可以直接倍增做,也就是需要知道某个点子树内的dp值,子树以外的dp值(也可以说是以这个点为整棵树的根的dp值),以及一个点的(2^k)次方级祖先到这个点父亲所构成的链以及链旁边的部分的dp值,每次询问分类讨论一下就好了 就4.7kb,太好写了

    有两种情况,如果某个点是lca,那么答案由下面那个点的子树+两点之间的链(这里的链均不包含端点)+lca扣除下面那个点所在子树的贡献的dp值构成;否则就是由两个点的子树+两点分别到lca之间的链+lca扣除两个点所在子树的贡献的dp值构成

    注意无解的判断,两个询问点相邻,且都为0

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    #include<map>
    #define il inline
    #define LL long long
    #define db double
    #define re register
    
    using namespace std;
    const int N=100000+10;
    il LL rd()
    {
      LL x=0,w=1;char ch=0;
      while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
      while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
      return x*w;
    }
    int to[N<<1],nt[N<<1],hd[N],tot=1;
    il void add(int x,int y)
    {
      ++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;
      ++tot,to[tot]=x,nt[tot]=hd[y],hd[y]=tot;
    }
    int n,nn,m,p[N],fa[N][20],sz[N],de[N],son[N],top[N];
    LL f[N][2],f0[N],f1[N],g[N][2],h[N][20][2][2],a1[2][2],a2[2][2],ff[2];
    void dfs1(int x)
    {
      sz[x]=1;
      for(int i=1;i<=nn;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
      for(int i=hd[x];i;i=nt[i])
    	{
    	  int y=to[i];
    	  if(y==fa[x][0]) continue;
    	  fa[y][0]=x,de[y]=de[x]+1,dfs1(y),sz[x]+=sz[y];
    	  if(sz[y]>sz[son[x]]) son[x]=y;
    	  f[x][0]+=f[y][1],f[x][1]+=min(f[y][0],f[y][1]);
    	}
      f[x][1]+=p[x];
      for(int i=hd[x];i;i=nt[i])
    	{
    	  int y=to[i];
    	  if(y==fa[x][0]) continue;
    	  f0[y]=f[x][0]-f[y][1],f1[y]=f[x][1]-min(f[y][0],f[y][1]);
    	}
    }
    void dfs2(int x,int ntp)
    {
      top[x]=ntp;
      if(x>1) g[x][0]=g[fa[x][0]][1]+f1[x],g[x][1]=min(g[fa[x][0]][0]+f0[x],g[fa[x][0]][1]+f1[x]);
      h[x][0][0][0]=f[fa[x][0]][0]-f[x][1],h[x][0][1][1]=f[fa[x][0]][1]-min(f[x][0],f[x][1]),h[x][0][0][1]=h[x][0][1][0]=1ll<<50;
      for(int i=1;i<=nn;i++)
    	{
    	  for(int j=0;j<=1;j++)
    		for(int k=0;k<=1;k++)
    		  {
    			h[x][i][j][k]=1ll<<50;
    			for(int l=0;l<=1;l++)
    			  for(int o=0;o<=1;o++)
    				if(o|l) h[x][i][j][k]=min(h[x][i][j][k],h[x][i-1][j][l]+h[fa[x][i-1]][i-1][o][k]);
    		  }
    	}
      if(son[x])
    	{
    	  dfs2(son[x],ntp);
    	}
      for(int i=hd[x];i;i=nt[i])
    	{
    	  int y=to[i];
    	  if(y==fa[x][0]||y==son[x]) continue;
    	  dfs2(y,y);
    	}
    }
    il int getlca(int x,int y)
    {
      while(top[x]!=top[y])
    	{
    	  if(de[top[x]]<de[top[y]]) swap(x,y);
    	  x=fa[top[x]][0];
    	}
      return de[x]<de[y]?x:y;
    }
    
    int main()
    {
      //freopen("defense.in","r",stdin);
      //freopen("defense.out","w",stdout);
      n=rd(),m=rd();
      nn=log2(n);
      char cc[3];
      scanf("%s",cc);
      for(int i=1;i<=n;i++) p[i]=rd();
      for(int i=1;i<n;i++) add(rd(),rd());
      dfs1(1),dfs2(1,1);
      while(m--)
    	{
    	  a1[0][0]=a1[0][1]=a1[1][0]=a1[1][1]=0;
    	  a2[0][0]=a2[0][1]=a2[1][0]=a2[1][1]=0;
    	  int x=rd(),a=rd(),y=rd(),b=rd(),lca=getlca(x,y);
    	  if(y==lca) swap(x,y),swap(a,b);
    	  if(x==lca)
    		{
    		  if(fa[y][0]==x&&!(a|b)) {puts("-1");continue;}
    		  int yy=y;
    		  for(int i=nn;i>=0;i--)
    			if(de[fa[yy][i]]>=de[x]+1)
    			  {
    				if(a1[0][0])
    				  {
    					LL nw[2][2];
    					nw[0][0]=nw[0][1]=nw[1][0]=nw[1][1]=1ll<<50;
    					for(int j=0;j<=1;j++)
    					  for(int k=0;k<=1;k++)
    						for(int l=0;l<=1;l++)
    						  for(int o=0;o<=1;o++)
    							if(l|o) nw[j][k]=min(nw[j][k],a1[j][l]+h[yy][i][o][k]);
    					for(int j=0;j<=1;j++)
    					  for(int k=0;k<=1;k++)
    						a1[j][k]=nw[j][k];
    				  }
    				else
    				  {
    					for(int j=0;j<=1;j++)
    					  for(int k=0;k<=1;k++)
    						a1[j][k]=h[yy][i][j][k];
    				  }
    				yy=fa[yy][i];
    			  }
    		  ff[0]=f[x][0]+g[x][0]-f[yy][1],ff[1]=f[x][1]+g[x][1]-min(f[yy][0],f[yy][1]);
    		  LL ans=1ll<<50;
    		  for(int j=0;j<=1;j++)
    			for(int k=0;k<=1;k++)
    			  if((b|j)&(a|k)) ans=min(ans,ff[a]+f[y][b]+a1[j][k]);
    		  printf("%lld
    ",ans<(1ll<<50)?ans:-1);
    		}
    	  else
    		{
    		  int xx=x,yy=y;
    		  for(int i=nn;i>=0;i--)
    			if(de[fa[xx][i]]>=de[lca]+1)
    			  {
    				if(a1[0][0])
    				  {
    					LL nw[2][2];
    					nw[0][0]=nw[0][1]=nw[1][0]=nw[1][1]=1ll<<50;
    					for(int j=0;j<=1;j++)
    					  for(int k=0;k<=1;k++)
    						for(int l=0;l<=1;l++)
    						  for(int o=0;o<=1;o++)
    							if(l|o) nw[j][k]=min(nw[j][k],a1[j][l]+h[xx][i][o][k]);
    					for(int j=0;j<=1;j++)
    					  for(int k=0;k<=1;k++)
    						a1[j][k]=nw[j][k];
    				  }
    				else
    				  {
    					for(int j=0;j<=1;j++)
    					  for(int k=0;k<=1;k++)
    						a1[j][k]=h[xx][i][j][k];
    				  }
    				xx=fa[xx][i];
    			  }
    		  for(int i=nn;i>=0;i--)
    			if(de[fa[yy][i]]>=de[lca]+1)
    			  {
    				if(a2[0][0])
    				  {
    					LL nw[2][2];
    					nw[0][0]=nw[0][1]=nw[1][0]=nw[1][1]=1ll<<50;
    					for(int j=0;j<=1;j++)
    					  for(int k=0;k<=1;k++)
    						for(int l=0;l<=1;l++)
    						  for(int o=0;o<=1;o++)
    							if(l|o) nw[j][k]=min(nw[j][k],a2[j][l]+h[yy][i][o][k]);
    					for(int j=0;j<=1;j++)
    					  for(int k=0;k<=1;k++)
    						a2[j][k]=nw[j][k];
    				  }
    				else
    				  {
    					for(int j=0;j<=1;j++)
    					  for(int k=0;k<=1;k++)
    						a2[j][k]=h[yy][i][j][k];
    				  }
    				yy=fa[yy][i];
    			  }
    		  ff[0]=f[lca][0]+g[lca][0]-f[xx][1]-f[yy][1],ff[1]=f[lca][1]+g[lca][1]-min(f[xx][0],f[xx][1])-min(f[yy][0],f[yy][1]);
    		  LL ans=1ll<<50;
    		  for(int j=0;j<=1;j++)
    			for(int k=0;k<=1;k++)
    			  for(int l=0;l<=1;l++)
    				for(int o=0;o<=1;o++)
    				  for(int p=0;p<=1;p++)
    					if((x!=xx?(a|j)&&(l|p):(a|p))&(y!=yy?(b|k)&&(o|p):(b|p))) ans=min(ans,f[x][a]+f[y][b]+a1[j][l]+a2[k][o]+ff[p]);
    		  printf("%lld
    ",ans<(1ll<<50)?ans:-1);
    		}
    	}
      return 0;
    }
    
    
  • 相关阅读:
    springboot配置tomcat大全
    python 列表推导式
    python中yield的用法详解——最简单,最清晰的解释
    正则表达式
    python 装饰器
    python 接口类、抽象类、多态
    python split和os.path.split()
    pyhton 多继承的执行顺序
    python unittest 加载测试用例的方法
    python unittest中的四个概念
  • 原文地址:https://www.cnblogs.com/smyjr/p/10009491.html
Copyright © 2011-2022 走看看