Codeforces Round #396 (Div. 2)

14 篇文章 0 订阅
9 篇文章 0 订阅

比赛链接:Codeforces Round #396 (Div. 2)

A:找最长不公共连续子序列。显然若s!=t,输出s和t最大长度,否则输出-1.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
	string s,t;
	while(cin>>s>>t)
	{
		if(s.length()>t.length())	cout<<s.length()<<endl;
		else if(s.length()<t.length())	cout<<t.length()<<endl;
		else if(s==t)	cout<<-1<<endl;
		else cout<<s.length()<<endl;
	}
	return 0;
}


B:题意是给定若干个线段长度,求能否从中选出三条线段组成一个三角形。

先将线段长度L排序,若长度为x,y,z的线段能组成三角形,显然z+y>x&&z-y<x  ,设y的下标为i,那么用L[i-1]来替代x,L[i+1]来替代z,显然条件仍前述条件仍满足。

因此可以得出结论:如果存在三条线段能组成三角形,那么在数组L中,必然存在连续的三条线段能组成三角形,那么我们检验就行。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int l[100007];
bool check(int a,int b,int c)
{
	if(a+b>c&&a+c>b&&b+c>a)	return true;
	else return false;
}
int main()
{
	int n;
	while(cin>>n)
	{
		for(int i=0;i<n;i++)	cin>>l[i];
		sort(l,l+n);
		int i;
		for(i=0;i<n-2;i++)
		{
			if(check(l[i],l[i+1],l[i+2]))
			{
				puts("YES");
				break;
			}
		}
		if(i==n-2)	puts("NO");
	}

	return 0;
}


C:题意是给定一个字符串,要求对字符串就行分割使得每对于每一个子串s,对于其中的每一个字符ch, a[ch]<=s.length() 。可以dp来做。

设dp[i]为串[0~i-1]能分割的方法数,len[i]为串[0~i-1]所能分割的最小子串数,并维护ans--整个串所能割出的最长子串的长度。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll dp[1007];
int a[26],len[1007],cnt[26],ans;
int f(char ch)
{
	return ch-'a';
}
bool check(int l)
{
	for(int i=0;i<26;i++)
		if(cnt[i]&&l>a[i])	return false;
	return true;
}
int main()
{
	int n;
	char s[1007];
	while(cin>>n)
	{
		scanf("%s",s+1);
		for(int i=0;i<26;i++)	cin>>a[i];
		memset(dp,0,sizeof(dp));
		for(int i=1;i<=n;i++)	len[i]=1007;
		ans=dp[0]=1;
		len[0]=0;
		for(int i=1;i<=n;i++)
		{
			memset(cnt,0,sizeof(cnt));
			for(int j=i;j>0;j--)
			{
				cnt[f(s[j])]++;
				if(check(i-j+1))
				{
					dp[i]=(dp[i]+dp[j-1])%mod;
					ans=max(ans,i-j+1);
					len[i]=min(len[i],len[j-1]+1);
				}
				else break;
			}
		}
		cout<<dp[n]<<endl;
		cout<<ans<<endl;
		cout<<len[n]<<endl;
	}
	return 0;
}


D:题意是给定一些词汇,并描述他们之间的关系,将矛盾的关系忽略。并且之后给定两个词,并查询他们之间的关系。

可以用并查集来做,词汇i的同义词是i,反义词是i+n,根据输入进行合并操作就行了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
map<string,int> T;
int father[maxn<<1];
int f(int x)
{
	if(x==father[x])	return x;
	father[x]=f(father[x]);
	return father[x];
}
void Union(int x,int y)
{
	x=f(x);y=f(y);
	father[x]=y;
}
int main()
{
	ios::sync_with_stdio(false);
	int n,m,q;
	while(cin>>n>>m>>q)
	{
		T.clear();
		int o;
		string s,t;
		for(int i=0;i<n;i++)
		{
			cin>>s;
			T[s]=i;
		}
		for(int i=0;i<n*2;i++)	father[i]=i;
		for(int i=0;i<m;i++)
		{
			cin>>o>>s>>t;
			int l=T[s],r=T[t];
			if(o==1)
			{
				if(f(l+n)==f(r)||f(r+n)==f(l))	cout<<"NO"<<endl;
				else
				{
					Union(l,r);
					Union(l+n,r+n);
					cout<<"YES"<<endl;
				}
			}
			else
			{
				if(f(l+n)==f(r+n)||f(l)==f(r))	cout<<"NO"<<endl;
				else
				{
					Union(l,r+n);
					Union(r,l+n);
					cout<<"YES"<<endl;
				}
			}
		}
		for(int i=0;i<q;i++)
		{
			cin>>s>>t;
			int l=T[s],r=T[t];
			if(f(l)==f(r)||f(l+n)==f(r+n))	cout<<1<<endl;
			else if(f(l)==f(r+n)||f(l+n)==f(r))	cout<<2<<endl;
			else cout<<3<<endl;
		}
	}
	return 0;
}


E:题意是给定一个带权树,求Sum(Xor(Path(i,j)))  {i<=j}。 Xor(Path(i,j))为 a_i^a_p_1^a_p_2^....a_p_k^a_j , 其中 i,p_1,p_2,...p_k,j为i到j的路径上一次经过的点。

对于每一个点的权值 val, val=Sum(bit[j]*(1<<j)) 。因此可以将每一位孤立出来进行求和。答案为Sum(ans[j]*(1<<j)) 。

因此可以来跑dfs并对每一位进行树形dp,记ans[j]为有第j为多少条路径的异或值为1,dp[i][j][k]为点i的第j位异或值为k的路径数(其中i必为一端点)。dp可以简单地通过其孩子的dp值来进行转移。

但i可能有多棵子树,假设u,v是i的不同子树中的结点,u,v也同样地构成路径。对于子树t, s为同根的其他子树。  x=使得Xor(t,root,s)=1的t和s对应异或值和位数j的路径数量之积。ans[j]+=x/2 。

要更容易理解的话,就是将每一位剖离出来单独进行一次dfs,然后把每一位dfs的值求和即可。最关键的就是要求出异或值为1的路径总数量

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[100007][23][2];
ll ans[23];
vector<int> adj[100007];
int arr[100007][23];
int dfs(int u,int p)
{
	ll t[23][2];
	memset(t,0,sizeof(t));
	for(int j=0;j<23;j++)	dp[u][j][arr[u][j]]=1;
	for(int i=0;i<adj[u].size();i++)
	{
		int v=adj[u][i];
		if(v==p)	continue;
		dfs(v,u);
		for(int j=0;j<23;j++)
		{
			for(int k=0;k<2;k++)
			{
				dp[u][j][k]+=dp[v][j][k^arr[u][j]];
				t[j][k]+=dp[v][j][k];
			}
		}
	}
	for(int j=0;j<23;j++)
	{
		ll x=0;
		for(int i=0;i<adj[u].size();i++)
		{
			int v=adj[u][i];
			if(p==v)	continue;
			for(int k=0;k<2;k++)
				x+=(t[j][k]-dp[v][j][k])*dp[v][j][1^k^arr[u][j]];
		}
		ans[j]+=x/2;
	}
	for(int j=0;j<23;j++)	ans[j]+=dp[u][j][1];
}
int main()
{
	ios::sync_with_stdio(false);
	int n,u,v;
	int a;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a;
		for(int j=0;j<23;j++)
		{
			arr[i][j]=a&1;
			a>>=1;
		}
	}
	for(int i=1;i<n;i++)
	{
		cin>>u>>v;
		adj[u].push_back(v);
		adj[v].push_back(u);
	}
	dfs(1,0);
	ll sum=0;
	for(int j=0;j<23;j++)	sum+=(1<<j)*ans[j];
	cout<<sum<<endl;
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值