zoukankan      html  css  js  c++  java
  • Codeforces Round #656 (Div. 3)

    Codeforces Round #656 (Div. 3)

    A. Three Pairwise Maximums

    题意

    给你三个数(x=max(a,b),y=max(b,c),z=max(c,a))
    构造(a,b,c)使得其满足题目给定的(x,y,z)

    题解

    讨论一下是否存在两个相等最大值就好了。
    事实上题目要求中说了可以不按顺序,那就更方便了(但是我没看见QwQ)

    #include<iostream>
    #include<cstdio>
    using namespace std;
    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 main()
    {
    	int T=read();
    	while(T--)
    	{
    		int x=read(),y=read(),z=read();
    		if(x!=y&&y!=z&&z!=x){puts("NO");continue;}
    		if(x==y&&x>=z){printf("YES
    %d %d %d
    ",x,1,z);continue;}
    		if(x==z&&x>=y){printf("YES
    %d %d %d
    ",1,x,y);continue;}
    		if(y==z&&y>=x){printf("YES
    %d %d %d
    ",x,1,z);continue;}
    		puts("NO");
    	}
    }
    

    B. Restore the Permutation by Merger

    题意

    你有一个([1,n])的排列(p)
    现在你有两个相同的排列(p),你可以在相对位置不变的情况下,把第二个(p)给任意插入到第一个(p)中。
    现在你得到了长度为(2n)的最终序列,还远出原始的(p)

    题解

    从前往后扫一遍,显然我们只需要按照每个数第一次出现的位置排序就行了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define ll long long
    using namespace std;
    #define MAX 111
    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 T,n,a[MAX];
    bool vis[MAX];
    int main()
    {
    	T=read();
    	while(T--)
    	{
    		n=read();
    		for(int i=1;i<=n+n;++i)a[i]=read();
    		memset(vis,0,sizeof(vis));
    		for(int i=1;i<=n+n;++i)if(!vis[a[i]])printf("%d ",a[i]),vis[a[i]]=true;
    		printf("
    ");
    	}
    	return 0;
    }
    

    C. Make It Good

    题意

    定义一个序列是好的,当且其存在某个位置(x),在([1,x])不降,在([x,n])不增
    现在给你一个序列,问最少删掉多长的前缀,使得剩下的部分是一个好的序列。

    题解

    转化一下就是最长的好的后缀。
    然后就模拟了。。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MAX 200200
    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,a[MAX];
    int main()
    {
    	int T=read();
    	while(T--)
    	{
    		n=read();
    		for(int i=1;i<=n;++i)a[i]=read();
    		a[n+1]=-1;
    		int p=n;while(p>1&&a[p-1]>=a[p])--p;
    		while(p>1&&a[p-1]<=a[p])--p;
    		printf("%d
    ",p-1);
    	}
    	return 0;
    }
    

    D. a-Good String

    题意

    解释起来有点复杂。。。。
    建议自己读一下原题

    题解

    直接(dp)就行了。设(f[i][j])表示当前考虑填的字母是('a'+i),其开头位置在(j)
    (i)层的状态数是(2^i)(只有这么多可选择的位置)
    总的状态数是(O(n))级别的。
    建议稍微压一下状态,然后我的代码就很丑了,原谅我。

    #include<iostream>
    #include<cstdio>
    #include<vector>
    using namespace std;
    #define MAX 200200
    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,sz[22],len[22];
    char c[MAX];
    vector<int> f[22];
    int main()
    {
    	int T=read();
    	while(T--)
    	{
    		n=read();scanf("%s",c+1);
    		if(n==1)
    		{
    			if(c[1]=='a')puts("0");
    			else puts("1");
    			continue;
    		}
    		int p=0;while((1<<p)<n)++p;
    		for(int i=0;i<p;++i)f[i].resize(sz[i]=(1<<(i+1))),len[i]=n>>(i+1);
    		f[p].resize(n);len[p]=len[p-1];sz[p]=sz[p-1];
    		for(int i=0;i<=p;++i)
    			for(int j=1,t=0;j<=n;j+=len[i],++t)
    			{
    				for(int k=j;k<j+len[i];++k)if(c[k]!=('a'+i))++f[i][t];
    				if(i!=0)
    				{
    					if(i!=p)f[i][t]+=f[i-1][(t>>1)^1];
    					else f[i][t]+=f[i-1][t^1];
    				}
    			}
    		int ans=n;
    		for(int i=0;i<n;++i)ans=min(ans,f[p][i]);
    		printf("%d
    ",ans);
    		for(int i=0;i<=p;++i)f[i].clear();
    	}
    }
    

    E. Directing Edges

    题意

    给你一张图。
    一些边定向,一些边还未定向。
    你要把未定向的边给定向,使得图是个(DAG)

    题解

    把图按照定向的边拓扑排序,未定向的边强制从拓扑序小的连向大的就可以了。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #define ll long long
    using namespace std;
    #define MAX 200200
    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,m;
    vector<int> E[MAX];
    int dg[MAX],p[MAX],rk[MAX];
    int U[MAX],V[MAX],O[MAX];
    queue<int> Q;
    bool Topsort()
    {
    	for(int i=1;i<=n;++i)
    		for(int j=0,l=E[i].size();j<l;++j)
    			dg[E[i][j]]++;
    	for(int i=1;i<=n;++i)if(!dg[i])Q.push(i);
    	int cnt=0;
    	while(!Q.empty())
    	{
    		int u=Q.front();Q.pop();
    		p[++cnt]=u;
    		for(int j=0,l=E[u].size();j<l;++j)
    			if(!--dg[E[u][j]])Q.push(E[u][j]);
    	}
    	return cnt==n;
    }
    int main()
    {
    	int T=read();
    	while(T--)
    	{
    		n=read();m=read();int cnt=0;
    		for(int i=1;i<=m;++i)
    		{
    			int o=read(),u=read(),v=read();
    			if(o)E[u].push_back(v);
    			++cnt,U[cnt]=u,V[cnt]=v;O[cnt]=o;
    		}
    		if(Topsort())
    		{
    			puts("YES");
    			for(int i=1;i<=n;++i)rk[p[i]]=i;
    			for(int i=1;i<=cnt;++i)
    				if(rk[U[i]]>rk[V[i]])swap(U[i],V[i]);
    			for(int i=1;i<=cnt;++i)printf("%d %d
    ",U[i],V[i]);
    		}
    		else puts("NO");
    		for(int i=1;i<=n;++i)E[i].clear(),dg[i]=0;
    	}
    	return 0;
    }
    
    

    F. Removing Leaves

    题意

    你有一棵树,每次你可以同时删去恰好和某一节点联通的(k)个叶子节点。
    问最多可以删除多少次。

    题解

    显然能够删我把它删去不会更差。
    然后直接模拟就行了。
    (set)维护相邻节点是一个很好的方法(也更加符合复杂度),因为你需要实时的删除掉相邻节点。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #define ll long long
    using namespace std;
    #define MAX 200200
    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 ls[MAX];
    set<int> E[MAX];
    void Add(int u,int v){E[u].insert(v);}
    int n,K;
    queue<int> Q;
    int main()
    {
    	int T=read();
    	while(T--)
    	{
    		n=read();K=read();
    		for(int i=1;i<n;++i)
    		{
    			int u=read(),v=read();
    			Add(u,v);Add(v,u);
    		}		
    		for(int i=1;i<=n;++i)if(E[i].size()==1)Q.push(i);
    		while(!Q.empty())
    		{
    			int v=Q.front();Q.pop();
    			int u=*E[v].begin();
    			++ls[u];E[u].erase(v);
    			if(E[u].size()==1&&ls[u]%K==0)Q.push(u);			
    		}
    		int ans=0;
    		for(int i=1;i<=n;++i)ans+=ls[i]/K,ls[i]=0,E[i].clear();
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    G. Columns Swaps

    题意

    你有一个(2*n)的网格,每个格子里有一个([1,n])之间的整数。
    现在问你能否通过若干次交换某一列上下的两个数,使得最终上下最终都恰好是([1,n])的一个排列。
    求最小的交换次数。

    题解

    加入我们只需要进行能不能的判断的话:
    首先先确定是否每个数字恰好存在两个,如果不是显然无解,否则只需要满足任意一行就行了(另一行必定互补)。
    对于某一行进行考虑,显然只需要每个数字恰好出现一次就行了。
    于是我们把每一列看成有两种状态:不交换和交换
    那么我们就得到了若干组限制,每个限制形如:
    假设第(i)行进行交换,则第(j)行必须进行交换;
    假如第(i)行进行交换,则第(j)行必须不进行交换;
    假设第(i)行不进行交换,则第(j)行必须进行交换;
    假如第(i)行不进行交换,则第(j)行必须不进行交换。
    这似乎是一个(2-sat)问题,拆点之后我们可以利用(Tarjan)算法进行可行性的判断。
    我们先做一个小定义,我们定义对于某个点(k)而言,如果(k>n)则其相反点为(k-n),否则为(k+n)
    由连边关系可知,这个图非常特殊,我们假设拆点之后(i)表示不换,(i+n)表示换。
    因为连边具有很强的对称性,所以可以直接用并查集维护这个东西(即,限制是双向的)。那么并查集维护完了之后(check)是否存在(i)(i+n)在同一个集合中,即可判断是否存在解。
    考虑接下来怎么求最小的交换次数。
    显然的,我们希望的是选到尽可能多的代表不交换的点,即编号(leq n)的点竟可能的被多选。我们把编号(leq n)的点权值看成(1),编号(gt n)的点的权值看成(0)
    于是我们需要找到若干个不冲突的联通快中权值和最大的方案。
    那么,由上述的图高度对称可知,对于某个连通块而言,将连通块内所有点取反,则取反后的连通块也必定存在,且恰好也是一个连通块。
    于是就可以贪心的选择代表着交换次数较小的那一个了。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #define ll long long
    using namespace std;
    #define MAX 400400
    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,a[3][MAX],cnt[MAX];
    struct P{int x,y;}p[MAX][3];
    int f[MAX],val[MAX],cho[MAX];
    int getf(int x){return (f[x]==x)?x:(f[x]=getf(f[x]));}
    void Link(int x,int y){if(getf(x)==getf(y))return;val[getf(y)]+=val[getf(x)];f[getf(x)]=getf(y);}
    int main()
    {
    	int T=read();
    	while(T--)
    	{
    		n=read();bool fl=true;
    		for(int i=1;i<=2;++i)
    			for(int j=1;j<=n;++j)a[i][j]=read();
    		for(int i=1;i<=2;++i)
    			for(int j=1;j<=n;++j)if(a[i][j]<1||a[i][j]>n)fl=false;
    		if(!fl){puts("-1");continue;}
    		for(int i=1;i<=n;++i)cnt[i]=0;
    		for(int i=1;i<=2;++i)
    			for(int j=1;j<=n;++j)p[a[i][j]][++cnt[a[i][j]]]=(P){i,j};
    		for(int i=1;i<=n;++i)if(cnt[i]!=2)fl=false;
    		int ans=0;
    		for(int i=1;i<=n+n;++i)f[i]=i,val[i]=(i<=n);
    		for(int i=1;i<=n;++i)
    			if(p[i][1].x==p[i][2].x)Link(p[i][1].y,p[i][2].y+n),Link(p[i][1].y+n,p[i][2].y);
    			else Link(p[i][1].y,p[i][2].y),Link(p[i][1].y+n,p[i][2].y+n);
    		for(int i=1;i<=n;++i)if(getf(i)==getf(i+n))fl=false;
    		if(!fl){puts("-1");continue;}
    		for(int i=1;i<=n+n;++i)cho[i]=0;
    		for(int i=1;i<=n;++i)
    			if(getf(i)==i)
    			{
    				if(val[getf(i)]<val[getf(i+n)])cho[getf(i)]=1;
    				else cho[getf(i+n)]=1;
    				ans+=min(val[getf(i)],val[getf(i+n)]);
    			}
    		printf("%d
    ",ans);
    		for(int i=1;i<=n;++i)if(cho[getf(i)])printf("%d ",i);
    		if(ans)puts("");
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    php 将英文引号成对转换为中文引号
    centos 6.2 x86_64 编译安装 httpd2.4.2时,apr报错
    PHP在通过非HTTP方式或多客户端的情况下,session的共享
    用c链接mysql
    多进程和多线程有什么区别
    进程和线程的区别
    linux中重要数据声明
    春节后返校第三天
    窗外下着雨——来到南京的第一篇
    中断门与陷阱门的区别
  • 原文地址:https://www.cnblogs.com/cjyyb/p/13423824.html
Copyright © 2011-2022 走看看