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]) 摆渡车

  • 相关阅读:
    在Win10的注册表编辑器中如何快速跳转到相关键值?
    使用winsw给Win10添加服务
    巧把任意程序添加到Win10控制面板(添加“系统配置”为例)
    在Win8.1开始屏幕添加电源按钮
    win10中,如何隐藏此电脑中的6个文件夹?
    Win10恢复这台电脑里的6个文件夹
    解决Office 2010安装报错1907,没有足够权限注册字体。
    C#面向对象(OOP)入门—第二天—多态和继承(继承)
    C#面向对象(OOP)入门—第一天—多态和继承(方法重载)
    OpenCV与Python之图像的读入与显示以及利用Numpy的图像转换
  • 原文地址:https://www.cnblogs.com/forward777/p/11664810.html
Copyright © 2011-2022 走看看