codeforces Round541 div2 部分题解(1131D+1131E+1131F)

D.Gourmet choice

题意:给定你 n + m n+m 个数的大小关系,有等于,大于和小于,让你构造出原序列,使得最大值尽可能的小。

题解:考虑相等的数都是等价的,那么我们不妨通过并查集把所有的相等的数缩成一个联通块 ( t a r j a n (tarjan 也可以),然后由于大于关系,比一个数大的数必须至少多1,那么我们对于一个不等关系,每次将小的数连向大的数,那么就会构成一张 d a g dag (存在环一定不合法),直接 d f s dfs 或者拓扑排序求一个最短路就 o k ok

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long

using namespace std;

inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}

const int maxn = 4010;
const int maxm = 2e6+1e2;

char s[2010][2010];
int fa[maxn];
int point[maxn],nxt[maxm],to[maxm];
int deep[maxn];
int cnt,n,m;
int in[maxn];
queue<int> q;

void addedge(int x,int y)
{
	nxt[++cnt]=point[x];
	to[cnt]=y;
	in[y]++;
	point[x]=cnt;
}

int find(int x)
{
	if (fa[x]!=x) fa[x]=find(fa[x]);
	return fa[x];
}

int ans[maxn];

bool tpsort()
{
	for (int i=1;i<=n+m;i++)
	{
		if (in[i]==0 && find(i)==i) q.push(i),ans[i]=1;
	}
	while (!q.empty())
	{
		int x = q.front();
		q.pop();
		for (int i=point[x];i;i=nxt[i])
		{
		   int p = to[i];
		   in[p]--;
		   ans[p]=max(ans[p],ans[x]+1);
		   if (!in[p])
		     q.push(p);
		}
	}
	int tot=0;
	for (int i=1;i<=n+m;i++)
	{
		if (in[i]!=0) tot++;
	}
	return tot==0;
}

int main()
{
  n=read(),m=read();
  for (int i=1;i<=n+m;i++) fa[i]=i;
  for (int i=1;i<=n;i++)
  {
  	  scanf("%s",s[i]+1);
  }
  for (int i=1;i<=n;i++) 
    for (int j=1;j<=m;j++)
    {
    	if (s[i][j]=='=')
    	{
    		int f1 = find(i);
    		int f2 = find(j+n);
    		if (f1==f2) continue;
    		fa[f1]=f2;
		}
	}
  for (int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
	    int f1 = find(i);
		int f2 = find(j+n);
    	if (s[i][j]=='<')
    	  addedge(f1,f2);
    	if (s[i][j]=='>')
    	  addedge(f2,f1);
	}
  if(tpsort())
  {
  	cout<<"Yes"<<endl;
  	for (int i=1;i<=n;i++) cout<<ans[find(i)]<<" ";
  	cout<<endl;
  	for (int i=1;i<=m;i++) cout<<ans[find(i+n)]<<" ";
  	cout<<endl;
  }
  else
  {
  	cout<<"No"<<endl;
  }
  return 0;
}

1131E - String Multiplication

题意:懒得翻译了

题解:考虑到我们求的是最长的相同的字母的长度,所以每一种字母的贡献可以分开考虑,那么不妨枚举每个字母进行答案的更新。
考虑同一个字母应该怎么计算答案,可以发现,如果我们进行一次乘法,如果下一个串全是当前字母,那么就可以直接 n o w + = ( n o w + 1 ) l e n now+=(now+1)*len n o w now 表示当前字母目前最长的 a n s ans 。表示我们可以直接用 第二个串包住所有 n o w now 的字符的新串来更新答案,类似 a a a + a + a a a . . . . aaa+a+aaa....
如果下一个串不全是当前字母,我们会发现,答案就是最长前缀+最长后缀+ ( n o w = = 1 ) (now==1) ,形如 a b a a + a + a b a a abaa+&#x27;a&#x27;+abaa 的形式

然后就可以做辣

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long

using namespace std;

inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}

const int maxn = 1e5+1e2;

string s[maxn];
int n,ans;

bool check(int x,char c)
{
	int len = s[x].length();
	for (int i=0;i<len;i++) 
	  if (s[x][i]!=c) return false;
	return true;
}

int main()
{
  n=read();
  for (int i=1;i<=n;i++) cin>>s[i];
  
  for (char i='a';i<='z';i++)//每一个字母单独考虑,维护前缀和后缀等信息 
  {
  	int pre=0,suf=0,now=0;
  	for (int j=1;j<=n;j++)
  	{
  		int len = s[j].length();
  		if(check(j,i))//如果全是一样的,那么原本的ans,就可以被ans+1个此串包起来,前缀和后缀同理 
  		{
  			pre+=(pre+1)*len;
			suf+=(suf+1)*len;
			now+=(now+1)*len; 
		}
		else
		{
			pre=0;suf=0;
			for (int k=0;k<len && s[j][k]==i;k++) pre++;
			for (int k=len-1;k>=0 && s[j][k]==i;k--) suf++;
			if (now!=0) //形如 ...... + ans[i]+ ......的形式 ans[i]和点都是一样的字母 
			  now=pre+suf+1;
			else 
			  now = max(pre,suf); //如 ...... + ans[i]+ ......的形式 ans[i]和点 不 是 一样的字母 ! 
			int ymh = 0;
			for (int k=0;k<len;k++) //串本身的ans 
			{
				if (s[j][k]!=i)
				  now = max(now,ymh),ymh=0;
				else ymh++;
			}
			now=max(now,ymh);
		}
		//cout<<i<<" "<<j<<" "<<now<<endl; 
	}
	ans=max(ans,now);
  }  
  cout<<ans;
  return 0;
}

1131F - Asya And Kittens

题意:qwq
题解:
题意可以直接转化成给你 n n 个点,每次连接两个点所在的联通块,使得最后的联通块是一条链

直接启发式合并,合并的时候,大的联通块挂在小的联通块的最深的点的底下,这样合并是正确的。qwq画图可知

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<cstdlib>
#define pb push_back
#define mk make_pair
#define ll long long
#define lson ch[x][0]
#define rson ch[x][1]

using namespace std;

inline int read()
{
   int x=0,f=1;char ch=getchar();
   while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
   while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
   return x*f;
}

const int maxn = 3e5+1e2;
const int maxm = 2*maxn;

int point[maxn],nxt[maxm],to[maxm];
int cnt,n,m;
int x[maxm],y[maxm];
int fa[maxn];
int in[maxn];
int size[maxn];
int deep[maxn];
int mx,mxpos;

void addedge(int x,int y)
{
  nxt[++cnt]=point[x];
  to[cnt]=y;
  point[x]=cnt;
}

int find(int x)
{
  if (fa[x]!=x) fa[x]=find(fa[x]);
  return fa[x]; 
}

void dfs(int x,int fa)
{
   deep[x]=deep[fa]+1;
   if(mx<deep[x]) mx=deep[x],mxpos=x;
   for (int i=point[x];i;i=nxt[i])
   {
     int p = to[i];
     if (p==fa) continue;
     dfs(p,x);
   }
}

void solve(int x,int fa)
{
   cout<<x<<" ";
   for (int i=point[x];i;i=nxt[i])
   {
      int p = to[i];
      if (p==fa) continue;
      solve(p,x);
   }
}

int main()
{
   n=read();
   for (int i=1;i<n;i++) x[i]=read(),y[i]=read();
   for (int i=1;i<=n;i++) fa[i]=i,size[i]=1;
   for (int i=1;i<n;i++)
   {
      int f1 = find(x[i]);
      int f2 = find(y[i]);
      if (size[f1]<size[f2]) swap(f1,f2);
      mx=0;
      dfs(f2,0);
      addedge(f1,mxpos);
      addedge(mxpos,f1);
      size[f2]+=size[f1];
      fa[f1]=mxpos;
   }
   int ff = find(1);
   solve(ff,0);
   return 0;
}
``

猜你喜欢

转载自blog.csdn.net/y752742355/article/details/87907313