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\n%d %d %d\n",x,1,z);continue;}
		if(x==z&&x>=y){printf("YES\n%d %d %d\n",1,x,y);continue;}
		if(y==z&&y>=x){printf("YES\n%d %d %d\n",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("\n");
	}
	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\n",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\n",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\n",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\n",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\n",ans);
		for(int i=1;i<=n;++i)if(cho[getf(i)])printf("%d ",i);
		if(ans)puts("");
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM