Codeforces Round #548 (Div. 2) (题解)

Codeforces Round #548 (Div. 2) (题解)

A. Even Substrings

题目大意

给出一条数字字符串,问其有多少数字子串使其代表的数字为偶数

解题思路

从前向后遍历,若第i未偶数,则答案增加i

AC代码

#include
using namespace std;
#define int long long
int32_t main()
{
	int n;
	cin>>n;
	string s;
	cin>>s;
	int ans=0;
	for(int i=0;i<n;i++)
	{
		if((s[i]-'0')%2==0) ans+=i+1;
	}
	cout<<ans<<endl;
}

B. Chocolates

题目大意

有n种巧克力,每种巧克力有 a i a_i ai个,序号较大的巧克力买的数量必须演的大于序号较小的巧克力的数量

解题思路

从后往前贪心,每次都尽可能多买巧克力,仅仅保证比后面的巧克力少即可

AC代码

#include
using namespace std;
int arr[200005];
int maxn;
#define int long long
int32_t main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>arr[i];
	int ans=0;
	maxn=0x3f3f3f3f;
	for(int i=n;i>=1;i--)
	{
		ans+=min(maxn,arr[i]);
		maxn=min(maxn,arr[i]);
		maxn--;
		if(maxn==0) break;
	}
	cout<<ans<<endl;
}
		

C. Edgy Trees

题目大意

给出一棵树,其边为红色或者黑色.现在给出一种定义,一种序列设为 [ a 1 , a 2 , . . . a k ] [a_1,a_2,...a_k] [a1,a2,...ak]现在有一条路径,其从 a 1 a_1 a1 a 2 a_2 a2 a 3 a_3 a3到…到 a k a_k ak,若这条路径上至少有一条为黑色则定义为好序列

解题思路

一段序列当其不为好序列时,当且仅当序列中所有的数都在同一个只有红色边的联通块中时.为此只需要所有的情况减去序列中所有的点都从一个红色联通块中选取的即可

AC代码

#include
using namespace std;
const int mod=1e9+7;
typedef long long LL;
typedef pair<int,int> pii;
vector<pii> G[100005];
vector<int> b;
bool vis[100005];
int sz[100005];
int t;
LL quick_pow(LL a,LL b)
{
	LL ans=1;
	a=a%mod;
	while(b)
	{
		if(b&1) ans=ans*a%mod;
		b>>=1;
		a=a*a%mod;
	}
	return ans;
}
void dfs(int v)
{
	vis[v]=1;
	t++;
	for(auto x:G[v])
	{
		int u=x.first;
		int w=x.second;
		if(!vis[u]&&!w)
		{
			dfs(u);
		}
	}
}
int main()
{
	int n,k;
	scanf("%d%d",&n,&k);
	LL ans=quick_pow(n,k);
	int u,v,w;
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n-1;i++)
	{
		cin>>u>>v>>w;;
		G[u].push_back(pii(v,w));
		G[v].push_back(pii(u,w));
	}
	t=0;
	for(int i=1;i<=n;i++)
	{
		if(!vis[i])
		{
			dfs(i);
			b.push_back(t);
			t=0;
		}
	}
	for(auto x:b)
	{
		ans=(ans-quick_pow(x,k)+mod)%mod;
	}
	cout<<ans<<endl;
}

D. Steps to One

题目大意

给出一个上限m,现在从1-m中随机取出一个数,向集合s中添加数字,直至集合中所有的数的gcd为1.问集合大小的期望

解题思路

集合大小的期望即是在加最后一个数字之前所有的数字的公因数为不同的值的集合大小的的和.做一遍容斥即可

AC代码

#include
using namespace std;
const int N=1e5+5;
const int mod =1e9+7;
typedef long long LL;
bool prime[N];
int p[N],tot;
int minn_prime[N];
void init()
{
	for(int i=2;i<N;i++) prime[i]=true;
	for(int i=2;i<N;i++)
	{
		if(prime[i]) p[tot++]=i,minn_prime[i]=i;
		for(int j=0;j<tot&&i*p[j]<N;j++)
		{
			prime[i*p[j]]=false;
			minn_prime[i*p[j]]=p[j];
			if(i%p[j]==0) break;
		}
	}
}
LL quick_pow(LL a,LL b)
{
	LL ans=1;
	while(b)
	{
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
int solve(int x)
{
	int tot=0;
	while(x!=1)
	{
		tot++;
		int pr=minn_prime[x];
		x/=pr;
		while(minn_prime[x]==pr) return -1;
	}
	return tot;
}
int main()
{
	LL ans=1;
	int m;
	init();
	scanf("%d",&m);
	for(int i=2;i<=m;i++)
	{
		int t=solve(i);
		if(t==-1) continue;
		else if(t%2==1) ans=(ans+(m/i)*(quick_pow(m-m/i,mod-2))%mod)%mod;
		else  ans=((ans-(m/i)*(quick_pow(m-m/i,mod-2))%mod)%mod+mod)%mod;
	}
	cout<<ans<<endl;
}

E. Maximize Mex

题目大意

现有n个学生,m个小组,每个学生有一个潜力值,和一个所属的小组.现在需要每天从各个小组中选出一位学生组成一个新的队伍,使得这个队伍的潜力值有最大的mex,其中mex定义为一个最小的在集合中出现的非负整数.现在有d天,每天会有一位学生退学,问每天最大的mex

解题思路

简单最大流问题.所有的以后会退学的学生都先不连边,所有稳定存在的学生都由其所在的小组向其潜力值进行连边每次增大mex的上界并由上界所代表的数值对汇点连边,如在残余网络上跑出的流量等于1则上界继续增加直至无法跑出流量为止.从第d天向前,每天连接上离去的学生所代表的边即可

AC代码

#include
using namespace std;
const int maxn=20005;
const int maxm=100005;
const int inf=0x3f3f3f3f;
struct Edge{
	int to,nxt,cap,flow;
}edge[maxm];
int tol;
int head[maxn];
void init(){
	tol=2;
	memset(head,-1,sizeof(head));
}
void AddEdge(int u,int v,int w,int rw=0){
	edge[tol].to=v;edge[tol].cap=w;edge[tol].flow=0;
	edge[tol].nxt=head[u];head[u]=tol++;
	edge[tol].to=u;edge[tol].cap=rw;edge[tol].flow=0;
	edge[tol].nxt=head[v];head[v]=tol++;
}
int Q[maxn];
int dep[maxn],cur[maxn],sta[maxn];
bool bfs(int s,int t,int n){
	int front=0,tail=0;
	memset(dep,-1,sizeof(dep[0])*(n+1));
	dep[s]=0;
	Q[tail++]=s;
	while(front<tail){
		int u=Q[front++];
		for(int i=head[u];i!=-1;i=edge[i].nxt){
			int v=edge[i].to;
			if(edge[i].cap>edge[i].flow&&dep[v]==-1){
				dep[v]=dep[u]+1;
				if(v==t) return true;
				Q[tail++]=v;
			}
		}
	}
	return false;
}
int dinic(int s,int t,int n){
	int maxflow=0;
	while(bfs(s,t,n)){
		for(int i=0;i<n;i++) cur[i]=head[i];
		int u=s,tail=0;
		while(cur[s]!=-1){
			if(u==t){
				int tp=inf;
				for(int i=tail-1;i>=0;i--)
				{
					tp=min(tp,edge[sta[i]].cap-edge[sta[i]].flow);
				}
				maxflow+=tp;
				for(int i=tail-1;i>=0;i--){
					edge[sta[i]].flow+=tp;
					edge[sta[i]^1].flow-=tp;
					if(edge[sta[i]].cap-edge[sta[i]].flow==0) tail=i;
				}
				u=edge[sta[tail]^1].to;
			}
			else if(cur[u]!=-1&&edge[cur[u]].cap>edge[cur[u]].flow&&dep[u]+1==dep[edge[cur[u]].to]){
				sta[tail++]=cur[u];
				u=edge[cur[u]].to;
			}
			else{
				while(u!=s&&cur[u]==-1) u=edge[sta[--tail]^1].to;
				cur[u] = edge [cur[u]].nxt;
			}
		}
	}
	return maxflow;
}
int p[5005],c[5005];
int le[5005];
bool vis[5005];
int aans[5005];
int main()
{
	int m,n;
	init();
	scanf("%d%d",&n,&m);
	int maxn=0;
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&p[i]);
		maxn=max(maxn,p[i]);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&c[i]);
	}
	int d;
	scanf("%d",&d);
	for(int i=1;i<=d;i++)
	{
		scanf("%d",&le[i]);
		vis[le[i]]=true;
	}
	int s=0,t=5000+m+2;
	for(int i=1;i<=m;i++) AddEdge(s,i,1);
	for(int i=1;i<=n;i++)
	{
		if(vis[i]) continue;
		AddEdge(c[i],p[i]+m+1,1);
	}
	int ans=1;
	AddEdge(m+1,t,1);
	int f=0;
	for(int i=d;i>=1;i--)
	{
		while(true)
		{
			f=dinic(s,t,t+2)+f;
			if(ans!=f) break;
			AddEdge(ans+m+1,t,1);
			ans++;
		}
		AddEdge(c[le[i]],p[le[i]]+m+1,1);
		aans[i]=ans-1;
	}
	for(int i=1;i<=d;i++)
	{
		cout<<aans[i]<<endl;
	}
}

你可能感兴趣的:(套题题解,codeforce)