zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 008

    AtCoder Grand Contest 008

    A - Simple Calculator

    翻译

    有一个计算器,上面有一个显示按钮和两个其他的按钮。初始时,计算器上显示的数字是(x),现在想把这个数字给变成(y)。两个按钮的作用分别是让这个数加一和把这个数取反。问最少的按按钮的次数。

    题解

    神仙特判题,想清楚再写。

    #include<iostream>
    using namespace std;
    int x,y,ans=2147483647;
    int main()
    {
    	cin>>x>>y;
    	if(x==y){puts("0");return 0;}
    	if(y>x)ans=min(y-x,abs(y+x)+1);
    	else ans=min(abs(x+y)+1,2+abs(x-y));
    	printf("%d
    ",ans);
    	return 0;
    }
    

    B - Contiguous Repainting

    翻译

    (n)个格子,每个格子上面都有一个数字,初始时所有格子都是黑色。每次可以选择连续的格子,把他们都染成黑色或者白色。最大化最终状态下黑格子上面的数字和。

    题解

    我们抛去一个长度为(K)的区间,那么显然的,除了这个区间之外的任何一个格子我们都能够控制它的颜色,而这个区间只能整体为白或者整体为黑,那么讨论一下即可。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define ll long long
    #define MAX 101000
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    ll ans,sum,s[MAX],ss[MAX];
    int n,k,a[MAX];
    int main()
    {
    	n=read();k=read();
    	for(int i=1;i<=n;++i)a[i]=read();
    	for(int i=1;i<=n;++i)s[i]=s[i-1]+(a[i]>0?a[i]:0);
    	for(int i=1;i<=n;++i)ss[i]=ss[i-1]+a[i];
    	for(int i=1;i<=n-k+1;++i)
    	{
    		sum=s[i-1]+(s[n]-s[i+k-1]);
    		if(ss[i+k-1]-ss[i-1]>0)sum+=ss[i+k-1]-ss[i-1];
    		ans=max(ans,sum);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

    C - Tetromino Tiling

    翻译

    (7)种俄罗斯方块,你现在要用他们拼出一个(2*k)的矩形,最大化(k)

    每种方块可以旋转但是不能对称的翻转。(输出的是(k/2))

    题解

    (C)(B)简单系列。

    首先(2*2)的是无脑放。剩下的可以放进去的显然只有(1*4)和两个(L)型的。发现这三个的匹配方法有两个自己匹配和三个各一个拼在一起匹配,算一下即可。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define ll long long
    ll ans;
    int a[8];
    int main()
    {
    	for(int i=1;i<=7;++i)scanf("%d",&a[i]);
    	ans+=a[2];ans+=2*(a[1]>>1);ans+=2*(a[4]>>1);ans+=2*(a[5]>>1);
    	if((a[1]&1)&&(a[4]&1)&&(a[5]&1))ans+=3;
    	else if((a[1]&1)&&(a[4]&1)&&(!(a[5]&1))&&a[5])ans+=1;
    	else if((a[1]&1)&&(!(a[4]&1))&&(a[5]&1)&&a[4])ans+=1;
    	else if((!(a[1]&1))&&(a[4]&1)&&(a[5]&1)&&a[1])ans+=1;
    	cout<<ans<<endl;
    	return 0;
    }
    

    D - K-th K

    翻译

    给定一个长度为(n)的序列(x),构造一个长度为(n*n)的序列(a),保证([1,n])中的每个数都恰好出现了(n)次,并且第(i)(i)出现的位置是(x[i])

    题解

    我说前面几题里面最傻逼的就是(D)题?

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define MAX 505
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int n,pos=1,a[MAX*MAX],p[MAX],x[MAX];
    bool vis[MAX];
    bool cmp(int a,int b){return x[a]<x[b];}
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;++i)x[i]=read(),a[x[i]]=i,p[i]=i;
    	sort(&p[1],&p[n+1],cmp);
    	for(int i=1;i<=n;++i)
    	{
    		int v=p[i];
    		for(int j=1;j<v;++j)
    		{
    			while(a[pos])++pos;
    			a[pos]=v;
    		}
    		if(pos>x[v]){puts("No");return 0;}
    	}
    	for(int i=1;i<=n;++i)
    	{
    		int v=p[i];
    		for(int j=1;j<=n-v;++j)
    		{
    			while(a[pos])++pos;
    			if(pos<x[v]){puts("No");return 0;}
    			a[pos]=v;
    		}
    	}
    	puts("Yes");
    	for(int i=1;i<=n*n;++i)printf("%d ",a[i]);puts("");
    	return 0;
    }
    

    E - Next or Nextnext

    翻译

    给定一个长度为(n)的序列(a)。求满足要么(P_i=a_i),要么(P_{P_i}=a_i)的排列(P)的个数。

    题解

    我们把所有的(i)(a_i)连边,那么对于一个合法的(P),两点之间要么跳一步,要么跳两步。

    那么我们考虑这两种跳法可以构成几种不同的情况(对于一个环考虑)。

    • 所有点都跳了一步。那么显然就是原图。
    • 所有点都跳了两部。那么如果当前的环是一个奇环,那么显然它跳完之后还是一个奇环,只不过是点的顺序改变了一下,即得到了一个同构的环。如果当前的环是一个偶环,那么他就被拆分成了两个小环。
    • 一部分是奇环,一部分是偶环。那么这样子会形成一个环,然后在它的外面形成了一些"脚",即连进来的一些边。同时所有的"脚"都不会从同一个节点伸展出来,并且所有的脚都是一条链的形式。

    (i)连向(a_i)构成的图称之为原图,(P)构建出来的图称为新图,显然原图是由若干个部分组成的。同理(P)构建出来的图也只能基于原图,通过上面三种情况(如果奇偶环分开考虑就是四种),归类之后只有两种情况,一种是环,另外一种是长出了"脚",即基环内向树,这些东西构成。

    先考虑原图构成的环,对于每个环要么就是原图,要么对于奇环而言是同构的图,要么是通过偶环被拆分成了两个等大小的图。那么我们把所有等大小的环一起考虑,考虑所有的方案数。在合并两个等大小的环的时候注意一下有环大小种方法合并。

    再考虑基环内向树的情况,显然两两之间不能合并,所以我们分开考虑每一个的答案。首先对于两条腿,我们最多只有两种方法将一条腿和环合并(这里画不了图,直接看(atcoder)上面的题解就有图)。现在考虑合并两条腿的情况,设(l1)为第二条腿的长度,(l2)是两条腿在挂在环上的那个点的之间的距离。如果(l1lt l2),那么有(2)种方法合并进来,如果(l1=l2),那么只有一种方法,否则(0)种方法。

    代码里面有注释。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define MOD 1000000007
    #define MAX 100100
    #define ll long long
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int n,ans=1;
    int a[MAX],dg[MAX];
    bool cir[MAX];
    int col[MAX],foot[MAX],cnt[MAX];
    int f[MAX];
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;++i)a[i]=read(),++dg[a[i]];
    	for(int i=1;i<=n;++i)
    	{
    		if(col[i])continue;
    		int x=i;
    		for(;!col[x];x=a[x])col[x]=i;
    		if(col[x]!=i)continue;//circle with foot
    		for(;!cir[x];x=a[x])cir[x]=true;//circle
    	}
    	for(int i=1;i<=n;++i)//illegal part 
    		if((cir[i]&&dg[i]>2)||(!cir[i]&&dg[i]>1)){puts("0");return 0;}
    	for(int i=1;i<=n;++i)
    	{
    		if(dg[i])continue;//start of a foot do not have egde in
    		int x=i,len=0;
    		while(!cir[x])x=a[x],++len;
    		foot[x]=len;//record length
    	}
    	for(int i=1;i<=n;++i)//calculate answer of circle with feet
    	{
    		if(!cir[i])continue;//calculate circles
    		int x=i,pts=0,st=0,l1=0,fr=0;
    		//st:last root of foot,fr:first root of foot,l1:length of the first foot
    		//pts:number of the node in the circle
    		while(cir[x])
    		{
    			++pts;cir[x]=false;
    			if(foot[x])//a foot in the circle
    			{
    				if(!fr)st=fr=pts,l1=foot[x];
    				else
    				{
    					int ways=(foot[x]<(pts-st))+(foot[x]<=(pts-st));
    					ans=ans*ways%MOD;st=pts;
    				}
    			}
    			x=a[x];
    		}
    		if(fr)//first foot of the circle
    		{
    			int ways=(l1<(pts+fr-st))+(l1<=(pts+fr-st));
    			ans=ans*ways%MOD;
    		}
    		else//just a circle
    			cnt[pts]+=1;
    	}
    	for(int i=1;i<=n;++i)//dp,in order to calc each length of circles
    	{
    		if(!cnt[i])continue;
    		f[0]=1;
    		for(int j=1;j<=cnt[i];++j)
    		{
    			if(i>1&&(i&1))//odd circle
    				f[j]=(f[j-1]<<1)%MOD;//two ways
    			else//even circle
    				f[j]=f[j-1];//only one way
    			if(j>1)f[j]=(f[j]+1ll*f[j-2]*(j-1)%MOD*i%MOD)%MOD;
    			//merge two circle in the same length
    		}
    		ans=1ll*ans*f[cnt[i]]%MOD;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    F - Black Radius

    题面

    给定一个(n)个节点的树。有些点可以染色,每次染色操作为选定一个可以染色的点,以及一个距离,把距离这个点不超过钦定值的所有点全部染黑。初始时所有点都是白色。恰好可以进行一次染色,求最终所有点染色情况的方案数。

    题解

    抄题解.jpg。

    定义(f(x,d))表示距离(x)不超过(d)的点集。显然答案就是询问本质不同的(f(x,d))的个数。考虑(S=f(x,d1)=f(y,d_2)),那么对于(x)(y)路径上的任何一点(K),都有(S=f(K,d_1-dist(x,K)))

    我们现在在假设所有点都可以染黑的情况下来讨论答案。首先(f(x,d))不考虑全集,最后再把全集加入答案即可。另外,对于(x)的任意一个相邻节点(v),不存在(f(x,d)=f(v,d-1))。第一个限制很容易理解。考虑如何理解第二个限制,根据上面那个有关于两者等价的式子,需要满足路径上任意两点都等于当前点集,那么,当不存在相邻点(v)满足(f(x,d)=f(v,d-1))的时候,证明(x)一定在这条路径的中点,因此也只会被考虑计算一次。

    满足第一个限制的(d)的范围很好计算,只需要求出距离当前点的最远点即可。考虑第二个限制如何计算。想清楚什么时候会有(f(x,d)=f(v,d-1))。如果以(x)为根,考虑距离(v)不超过(d-1)的所有点,如果除(v)自己的子树之外,所有(x)的子树都被染黑了,那么此时等式成立。那么在第二个限制中,(d)的范围是什么呢?显然不能超过到达它自己子树的最远距离。

    这样只考虑了(d)的上界,而在一个点可以被染黑的时候,显然(d)的下界是(0)。现在考虑(d)不能被染黑时它的下界。我们先钦定(x)作为当前的树根。如果(f(x,d))合法的话,必定满足一个可以被染黑的节点它的整棵子树都被染黑了,那么这里的下界就是这个可以被染黑的子树的最大深度。

    然而我也不太懂(抄题解)

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define ll long long
    #define MAX 200100
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    const int inf=1e9;
    struct Line{int v,next;}e[MAX<<1];
    int h[MAX],cnt=1,dg[MAX];
    inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;++dg[u];}
    int n,c[MAX],fa[MAX],sz[MAX];ll ans;
    int S[MAX],top;
    char s[MAX];
    int d1[MAX];//子树中距离最远的一个点
    int d2[MAX];//同上,可以从父亲转移
    int d3[MAX];//子树中有经过至少一个黑点的最远点
    int d5[MAX];//上界
    int d6[MAX];//下界
    void dfs1(int u,int ff)
    {
    	fa[u]=ff;sz[u]=c[u];
    	if((u!=1&&dg[u]==1)||(u==1&&!dg[u]))//叶子节点
    	{
    		d1[u]=0;d3[u]=c[u]?0:inf;
    		return;
    	}
    	int mx=0,mx2=inf;
    	for(int i=h[u];i;i=e[i].next)
    	{
    		int v=e[i].v;if(v==ff)continue;
    		dfs1(v,u);sz[u]+=sz[v];
    		mx=max(mx,d1[v]);
    		if(d3[v]<inf)mx2=min(mx2,d1[v]);
    	}
    	d1[u]=mx+1;d3[u]=mx2+1;
    	if(c[u])d3[u]=min(d3[u],d1[u]);
    }
    void dfs2(int u,int ff)
    {
    	d2[u]=max(d2[u],(ff!=0)+d2[ff]);//从父亲转移过来
    	if(ff)d5[u]=max(d5[u],d2[u]-1);//维护上界
    	top=0;for(int i=h[u];i;i=e[i].next)if(e[i].v!=ff)S[++top]=e[i].v;
    	for(int i=1,mx=-1;i<=top;++i)//前缀其他儿子转移一下
    	{
    		int v=S[i];
    		d2[v]=max(d2[v],mx+2);
    		mx=max(mx,d1[v]);
    	}
    	for(int i=top,mx=-1;i>=1;--i)//后缀其他儿子转移一下
    	{
    		int v=S[i];
    		d2[v]=max(d2[v],mx+2);
    		mx=max(mx,d1[v]);
    	}
    	for(int i=h[u];i;i=e[i].next)
    		if(e[i].v!=ff)dfs2(e[i].v,u);
    }
    int main()
    {
    	n=read();
    	for(int i=1;i<n;++i)
    	{
    		int u=read(),v=read();
    		Add(u,v);Add(v,u);
    	}
    	scanf("%s",s+1);int tot=0;
    	for(int i=1;i<=n;++i)tot+=(c[i]=s[i]-48);
    	if(!tot){puts("0");return 0;}
    	memset(d3,63,sizeof(d3));
    	dfs1(1,0);dfs2(1,0);
    	for(int i=1;i<=n;++i)
    	{
    		if(c[i])d6[i]=0;//可以涂色的点
    		else d6[i]=min(d3[i],(sz[i]==sz[1])?inf:d2[i]);
    		//不能涂色的点考虑下界
    		int u=max(d1[i],d2[i])-1;//u为上界,初始为最短点距离减一
    		for(int j=h[i];j;j=e[j].next)
    		{
    			int v=e[j].v;
    			if(v==fa[i])u=min(u,d1[i]+1);//如果从父亲转移过来,
    			else u=min(u,d5[v]+1);
    		}
    		if(u>=d6[i])ans+=u-d6[i]+1;
    	}
    	cout<<ans+1<<endl;
    	return 0;
    }
    
  • 相关阅读:
    Angular9 cdk-virtual-scroll-viewport' is not a known element 报错解决方案
    angular8打包时提示ERROR in Child compilation failed:解决方案
    angular8配置proxy本地跨域代理
    Vue-cli 本地跨域配置方法
    axios中url参数变量配置
    在vue-cli中使用axios时报错TypeError: Cannot set property 'lists' of undefined at eval
    nuxt.js element-ui踩坑记录(已解决)
    nuxt 关闭ESlint 语法检测
    javaScript学习之正则表达式初探
    直接在低版本IE6/7/8浏览器中使用HTML5的audio和video标签播放视频音频的办法
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9699261.html
Copyright © 2011-2022 走看看