zoukankan      html  css  js  c++  java
  • $NOIp$普及组做题记录

    ([NOIp2014]) 螺旋矩阵

    (Sol)

    直接模拟,一次走一整行或者一整列.复杂度(O(n)).

    (Code)

    #include<bits/stdc++.h>
    #define il inline
    #define Ri register int
    #define go(i,a,b) for(Ri i=a;i<=b;++i)
    #define yes(i,a,b) for(Ri i=a;i>=b;--i)
    #define e(i,u) for(Ri i=b[u];i;i=a[i].nt)
    #define mem(a,b) memset(a,b,sizeof(a))
    #define ll long long
    #define db double
    #define inf 2147483647
    using namespace std;
    il int read()
    {
        Ri x=0,y=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
        return x*y;
    }
    int n,s,x,z,y,nx,ny,tx,ty,nd,ex,ey,nw;
    il bool ck()
    {
        Ri x1=nx,x2=tx,y1=ny,y2=ty;
        if(x1>x2)swap(x1,x2);if(y1>y2)swap(y1,y2);
        return (ex>=x1 && ex<=x2 && ey>=y1 && ey<=y2);
    }
    il int calc(){return abs(nx-ex)+abs(ny-ey);}
    int main()
    {
        n=read(),ex=read(),ey=read();
        if(ex==1 && ey==1){printf("1
    ");return 0;}
        nx=ny=nd=1;nw=1;
        while(nd<=n*n)
        {
    		if(nw==1){tx=nx,ty=n-y;++s;}
    		if(nw==2){tx=n-x,ty=ny;++y;}
    		if(nw==3){tx=nx,ty=z+1;++x;}
    		if(nw==4){tx=s+1,ty=ny;++z;}
    		if(ck()){printf("%d
    ",nd+calc());break;}
    		nd+=abs(nx-tx)+abs(ny-ty);nx=tx,ny=ty;++nw;if(nw>4)nw=1;
        }
        return 0;
    }
    	
    

    ([NOIp2014])子矩阵

    (Sol)

    朴素地搜索可以获得(80pts).想想搜索的优化其实并不多(其实是我会的很少).所以这题可以直接记忆化搜索.具体来说,先把要选的行搜出来,然后搜要选的列的时候记忆化就行.

    (upd:)似乎搜行的时候也可以记忆化.不过搜列记忆化足以通过此题.

    (Code)

    #include<bits/stdc++.h>
    #define il inline
    #define Ri register int
    #define go(i,a,b) for(Ri i=a;i<=b;++i)
    #define yes(i,a,b) for(Ri i=a;i>=b;--i)
    #define e(i,u) for(Ri i=b[u];i;i=a[i].nt)
    #define mem(a,b) memset(a,b,sizeof(a))
    #define ll long long
    #define db double
    #define inf 2100000000
    using namespace std;
    il int read()
    {
        Ri x=0,y=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
        return x*y;
    }
    const int N=20;
    int n,m,r,c,a[N][N],b[N],d[N],as=inf,ct,f[20][20];
    il void init(){mem(f,-1);}
    il int dfs2(Ri nw,Ri lst,Ri sum)
    {
        if(nw>r)return 0;
        Ri ret=inf;
        if(f[nw][lst]!=-1)return f[nw][lst];
        go(i,lst+1,n-r+nw)
        {
    		d[nw]=i;Ri tmp=0;bool fl=1;
    		go(j,2,c){tmp+=abs(a[i][b[j]]-a[i][b[j-1]]);if(tmp>=as){fl=0;break;}}
    		if(!fl)continue;
    		if(nw!=1)go(j,1,c){tmp+=abs(a[i][b[j]]-a[d[nw-1]][b[j]]);if(tmp>=as){fl=0;break;}}
    		if(!fl)continue;
    		ret=min(ret,dfs2(nw+1,i,tmp)+tmp);
        }
        return f[nw][lst]=ret;
    }
    il void dfs1(Ri nw,Ri lst)
    {
        if(nw>c){init();as=min(dfs2(1,0,0),as);return;}
        go(i,lst+1,m-c+nw)
        	b[nw]=i;dfs1(nw+1,i);
    }
    int main()
    {
        n=read(),m=read(),r=read(),c=read();
        go(i,1,n)go(j,1,m)a[i][j]=read();
        dfs1(1,0);
        printf("%d
    ",as);
        return 0;	
    }
    
    

    ([NOIp2015]) 推销员

    (Sol)

    一个比较直接的想法是对于每个询问,枚举走得最远的位置,然后其他的直接选(这里可以用线段树维护一下)....这样做的复杂度大约是(O(n^2logn)),获得(60pts)问题不大.

    这样考虑:先直接选择(A_i)(X)大的.然后尝试舍去第(X)大的(A_i)来选择一个更远的.设选(A_i)(X)大的贡献是(sum A+D*2),现在要舍弃(x)来换取(y),那么这样做可以使得贡献增加((D_y-D)*2-A_x+A_y).选取一个贡献最大的即可.实际上只要选取(D_y*2+A_y)最大的即可,这样可以直接用一个数组维护.

    (Code)

    #include<bits/stdc++.h>
    #define il inline
    #define Ri register int
    #define go(i,a,b) for(Ri i=a;i<=b;++i)
    #define yes(i,a,b) for(Ri i=a;i>=b;--i)
    #define e(i,u) for(Ri i=b[u];i;i=a[i].nt)
    #define mem(a,b) memset(a,b,sizeof(a))
    #define ll long long
    #define db double
    #define inf 2147483647
    using namespace std;
    il int read()
    {
        Ri x=0,y=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
        return x*y;
    }
    const int N=100010;
    int n,ct,sa[N],maxd[N],maxf[N];
    struct nd{int a,d;}t[N];
    il bool cmp1(nd x,nd y){return x.d<y.d;}
    il bool cmp(nd x,nd y){return x.a>y.a;}
    int main()
    {
        n=read();
        go(i,1,n)t[i].d=read();
        go(i,1,n)t[i].a=read();
        sort(t+1,t+n+1,cmp);
        go(i,1,n)if(t[i].a==0){n=i-1;break;}
        go(i,1,n)sa[i]=sa[i-1]+t[i].a,maxd[i]=max(maxd[i-1],t[i].d);
        yes(i,n,1)maxf[i]=max(maxf[i+1],t[i].d*2+t[i].a);
        go(i,1,n)printf("%lld
    ",max(sa[i]+maxd[i]*2,sa[i-1]+maxf[i+1]));
        return 0;
    }
    
    

    ([NOIp2015])求和

    (Sol)

    简化下题意,纸带的分数是:(sum_{i<j,(j-i)&1=0}(i+j)(num_i+num_j)).

    直接枚举相同颜色的(i,j),(check (j-i))是否为偶数累加答案,可以获得(80pts).如果可以省去(check())这一步就好了.其实可以按照奇偶分组,同一组内,只要颜色相同就可以对答案产生贡献.

    假设([l,r])区间都是一种颜色,答案累加:

    (sum_{i=l}^{r}sum _{j=i+1}^r(i+j)(num_i+num_j))

    (=sum_{i=l}^r i*num_i*(r-l-1)+sum_{i=l}^{r}sum _{j=i}^r i*num_j)

    (=sum_{i=l}^r i*num_i*(r-l-1)+sum_{i=l}^{r}i *sum_{i=l}^r num_i)

    注意特判(l=r)的情况.

    (Code)

    #include<bits/stdc++.h>
    #define il inline
    #define Ri register int
    #define go(i,a,b) for(Ri i=a;i<=b;++i)
    #define ll long long	if(ct>1)as+=1LL*s3*(ct-2)%mod+1LL*s1*s2%mod;as%=mod;s1=s2=s3=ct=0;
    
    using namespace std;
    il int read()
    {
        Ri x=0,y=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
        return x*y;
    }
    const int N=100010,mod=10007;
    int n,m,as,n1,n2;
    struct nd{int num,col,pos;}a[N],b[N];
    il bool cmp(nd x,nd y){return x.col<y.col;}
    int main()
    {
        freopen("1.in","r",stdin);
        n=read(),m=read();
        go(i,1,n)if(i&1)a[++n1].num=read(),a[n1].pos=i;else b[++n2].num=read(),b[n2].pos=i;
        n1=n2=0;
        go(i,1,n)if(i&1)a[++n1].col=read();else b[++n2].col=read();
        sort(a+1,a+n1+1,cmp);sort(b+1,b+n2+1,cmp);
        Ri s1=0,s2=0,s3=0,ct=0;
        go(i,1,n1)
        {
    	s1+=a[i].num,s2+=a[i].pos,s3+=1LL*a[i].num*a[i].pos%mod,++ct;
    	s1%=mod,s2%=mod,s3%=mod;
    	if(a[i].col==a[i+1].col)continue;
    	if(ct>1)as+=1LL*s3*(ct-2)%mod+1LL*s1*s2%mod;as%=mod;s1=s2=s3=ct=0;
        }
        s1=s2=s3=ct=0;
        go(i,1,n2)
        {
    	s1+=b[i].num,s2+=b[i].pos,s3+=1LL*b[i].num*b[i].pos%mod,++ct;
    	s1%=mod,s2%=mod,s3%=mod;
    	if(b[i].col==b[i+1].col)continue;
    	if(ct>1)as+=1LL*s3*(ct-2)%mod+1LL*s1*s2%mod;as%=mod;s1=s2=s3=ct=0;
        }
        printf("%d
    ",as);
        return 0;
    }
    
    

    ([NOIp2016])海港

    (Sol)

    开一个队列存以当前时间为结束的(24)小时内到达的游客(包括时间和国际).一个数组(s[i])表示当前队列中国籍为(i)的游客有多少.一个变量(nw)记录当前队列中有多少不同国籍的游客.因为保证输入时间是递增的可以在线做,没必要离线做.每次来了一条新船,就把队首时间不合法的游客移除,顺便维护下(s[i]),如果有(s[i])因此变为(0)了,那么就(--nw).然后在队尾加入这条船的游客,维护$s[i] (,若有)s[i]$因此变为(1),那么(++nw).最后直接输出(nw),进入下一次循环即可.

    (Code)

    #include<bits/stdc++.h>
    #define il inline
    #define Ri register int
    #define go(i,a,b) for(Ri i=a;i<=b;++i)
    using namespace std;
    il int read()
    {
        Ri x=0,y=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
        return x*y;
    }
    const int N=100010;
    int n,ct,q[N*3],l=1,r,s[N*3],nw;
    struct nd{int t,k;}a[N*3];
    int main()
    {
        n=read();
        go(i,1,n)
        {
    	Ri t=read(),k=read();
    	go(i,1,k)
    	{
    	    Ri kk=read();
    	    a[++ct]=(nd){t,kk};q[++r]=ct;
    	    ++s[kk];if(s[kk]==1)++nw;
    	}
    	while(a[q[l]].t<=t-86400){if(!(--s[a[q[l]].k]))--nw;++l;}
    	printf("%d
    ",nw);
        }
        return 0;
    }
    
    

    ([NOIp2017]) 棋盘 ​

    $Sol $

    看起来十分像一道搜索题.需要记录地状态:当前在哪一个格子,已经花费多少金币,当前可不可以使用魔法.还要标记走过的格子.这样没有任何剪枝地搜就可以获得(55pts)的好成绩.

    加一个最优性剪枝就可以多获得(10pts).

    记一个(rem[i][j])表示从((1,1))((i,j))这个点花的最小代价,如果再搜到这里的时候代价大于它,那么就(return).这样做不仅可以剪枝,而且还可以少记一个(vis),于是少维护(vis)又可以加快速度.

    (upd):这样做当且仅当再走到这里的时候代价一定大于原来的情况,可是这题不一定叭?假如有一个同颜色的环,这样还是会死循环下去.

    (再upd),其实这里是可以按上面的做法的,只是一定是代价大于等于的时候(return),而且这样的话一定不可以记(vis).

    另外一个要注意的点(特别是如果要维护$vis (的话):不优的情况与其)dfs(下去再判掉不如**在)dfs$之前就判掉.**

    (Code)

    #define il inline
    #define Ri register int
    #define go(i,a,b) for(Ri i=a;i<=b;++i)
    #define yes(i,a,b) for(Ri i=a;i>=b;--i)
    #define e(i,u) for(Ri i=b[u];i;i=a[i].nt)
    #define mem(a,b) memset(a,b,sizeof(a))
    #define ll long long
    #define db double
    #define inf 2147483647
    using namespace std;
    il int read()
    {
        Ri x=0,y=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
        return x*y;
    }
    const int N=110;
    int m,n,col[N][N],dx[4]={1,0,-1,0},dy[4]={0,1,0,-1},as=inf,rem[N][N];
    il void dfs(Ri nx,Ri ny,Ri color,Ri sum,bool mag)
    {
        if(sum>=as)return;
        if(nx==ny && nx==m){as=min(as,sum);return;}
        rem[nx][ny]=sum;
        go(i,0,3)
        {
    		Ri tx=nx+dx[i],ty=ny+dy[i];
    			if(tx<1 || tx>m || ty<1 || ty>m)continue;
    	if(col[tx][ty])
    	{
    		    if(col[tx][ty]==color && (rem[tx][ty]==-1 || sum<rem[tx][ty]))
    		    {dfs(tx,ty,col[tx][ty],sum,1);}
    		    else if(rem[tx][ty]==-1 || sum+1<rem[tx][ty])
    		    {dfs(tx,ty,col[tx][ty],sum+1,1);}
    	}
    	else if(mag && (rem[tx][ty]==-1 || sum<rem[tx][ty]))
    	{dfs(tx,ty,color,sum+2,0);}
        }
    }
    int main()
    {
        m=read(),n=read();mem(rem,-1);
        go(i,1,n){Ri x=read(),y=read(),c=read();col[x][y]=c+1;}
        dfs(1,1,col[1][1],0,1);
        if(as==inf)as=-1;
        printf("%d
    ",as);
        return 0;
    }
    
    

    ([NOIp2017]) 跳房子 ​

    (Sol)

    首先判断有无解就是把所有的分数加起来与(k)比较.

    部分分是可以搜索的,就搜索出依次跳到哪些格子可以至少获得(k)分,然后统计一下答案就可以了.这样就获得了(20pts).

    觉得可以(dp.jpg).

    (f[i][j])表示前(i)个格子,且选了第(i)个格子,花费(j)改进,可以获得的最大价值.

    (f[i][j]=max (f[k][g])+a[i],k<i 且 abs(x[i]-x[k]-d)<=j且g<=j).

    然后(max(f[k][g]))这里可以用单调队列优化一下?

    然而这样并不足以(AC),时间空间上都有问题.如果能在(f)上省去一维就好了.真的不会,于是去看了题解.

    理解题解之后的:其实上述(dp)的第二维相当于在枚举最小花费,又应为最小花费显然可以二分.所以简单来说:首先二分最小花费,然后对这个最小花费(dp),最后统计答案.(over).

    (QwQ)

    写代码自闭了,真的要仔仔细细认认真真地思考一遍再写代码吖.几乎每次对代码掉以轻心,觉得直接写就好了的时候就会卡很久,到处出错,实际花的时间多得多.别忘了你是个菜鸡啊!写代码要沉着,冷静,放慢速度.

    (Code)

    #include<bits/stdc++.h>
    #define il inline
    #define Ri register int
    #define go(i,a,b) for(Ri i=a;i<=b;++i)
    #define yes(i,a,b) for(Ri i=a;i>=b;--i)
    #define e(i,u) for(Ri i=b[u];i;i=a[i].nt)
    #define mem(a,b) memset(a,b,sizeof(a))
    #define ll long long
    #define inf 2147483647
    using namespace std;
    il int read()
    {
        Ri x=0,y=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
        return x*y;
    }
    const int N=500010;
    int n,d,k,x[N],s[N],f[N],as=inf,L,R,mid,q[N];
    il bool Ck()
    {
        ll sum=0;Ri las=0;
        yes(i,n,1)if(s[i]>0){sum+=s[i];R=max(R,abs(x[i]-x[las]-d));las=i;};
        return (sum>=k);
    }
    il bool ck(Ri g)
    {
        go(i,1,n)f[i]=-inf;f[0]=0;
        Ri l=1,r=0,las=0;
        go(i,1,n)
        {
    		while(l<=r && x[q[l]]<x[i]-d-g)l++;
    		while(las<=n && x[las]<x[i]-d-g)las++;
    		while(las<i && x[las]<=x[i]-d+g )
    		{
    	  		  if(f[las]==-inf){++las;continue;}
    	  		  while(l<=r && f[q[r]]<=f[las])r--;
    	   		 q[++r]=las;++las;
    		}
    		if(l<=r)f[i]=f[q[l]]+s[i];
    		if(f[i]>=k)return 1;
        }
        return 0;
    }
    int main()
    {
        n=read(),d=read(),k=read();
        go(i,1,n)x[i]=read(),s[i]=read();
        if(!Ck()){printf("-1
    ");return 0;}
        while(L<=R)
        {
    		mid=(L+R)>>1;
    		if(ck(mid))as=mid,R=mid-1;
        }
        printf("%d
    ",as);
        return 0;
    }
    
    

    ([NOIp2018])对称二叉树

    (Sol)

    最初以为只要中序遍历是回文串就是对称二叉树,于是写了下只有(64pts).其实上面那个结论错得挺显然的.应该是这样的:首先是根的左孩子和右孩子相等,然后左孩子的左孩子和右孩子的右孩子相等,左孩子的右孩子和右孩子的左孩子相等.....这个可以以每个结点作为根(check)一次.本来打算先写个暴力,结果就(AC)辣.感觉复杂度似乎是(O(nlogn)).

    还有个(O(n))(hash)做法,其实我感觉就是我最开始那个回文串做法的改进.可以直接(hash)树的形态,举个栗子:一棵三个结点的二叉树(root,lson,rson),那么其(hash)值为(lson*bas1+rson*bas2+root*bas3).要计算两个(hash)值,一个是先走左儿子的中序遍历值(hash1),一个是先走右儿子的(hash2),对于(root),若(hash1[lson]=hash2[rson])那么就是对称二叉树.另外,记一下(TJ)中用的(bas),(b1=999999751,b2=299999827,b3=100000007,m1=89999794200117649,m2=999999786000011449).

    (Code)

    #include<bits/stdc++.h>
    #define il inline
    #define Ri register int
    #define go(i,a,b) for(Ri i=a;i<=b;++i)
    #define yes(i,a,b) for(Ri i=a;i>=b;--i)
    #define e(i,u) for(Ri i=b[u];i;i=a[i].nt)
    #define mem(a,b) memset(a,b,sizeof(a))
    #define ll long long
    #define db double
    #define inf 2147483647
    using namespace std;
    il int read()
    {
        Ri x=0,y=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
        return x*y;
    }
    const int N=1000010;
    int n,l[N],r[N],w[N],ct,sz[N],as;
    il void dfs(Ri nw)
    {
        sz[nw]=1;
        if(l[nw]>-1)dfs(l[nw]),sz[nw]+=sz[l[nw]];
        if(r[nw]>-1)dfs(r[nw]),sz[nw]+=sz[r[nw]];
    }
    il int ck(Ri x,Ri y)
    {
        if(x==-1 && y==-1)return 1;
        if(x==-1 || y==-1)return 0;
        if(w[x]!=w[y])return 0;
        if(ck(l[x],r[y]) && ck(r[x],l[y]))return 1;
        return 0;
    }
    int main()
    {
        n=read();
        go(i,1,n)w[i]=read();
        go(i,1,n)l[i]=read(),r[i]=read();
        dfs(1);
        go(i,1,n)if(ck(l[i],r[i]))as=max(as,sz[i]);
        printf("%d
    ",as);
        return 0;
    }
    
    

    ([NOIp2018]) 摆渡车

  • 相关阅读:
    LeetCode 275. H-Index II
    LeetCode 274. H-Index
    LeetCode Gray Code
    LeetCode 260. Single Number III
    LeetCode Word Pattern
    LeetCode Nim Game
    LeetCode 128. Longest Consecutive Sequence
    LeetCode 208. Implement Trie (Prefix Tree)
    LeetCode 130. Surrounded Regions
    LeetCode 200. Number of Islands
  • 原文地址:https://www.cnblogs.com/forward777/p/11664810.html
Copyright © 2011-2022 走看看