Educational Codeforces Round 94 题解

终于补完这场了(bushi

A:

题意:

定义相似(similar) 是有一个相同位置的字母相同。

你要构造一个字符串 \(w\) 使得和所有 \(s\) 中长度为 \(n\) 的子串相似(\(s\) 的长度为 \(2 \times n-1\))。

观察法可得是 \(s\{1,3,5,7,...,2\times n-1\}\)

#include<cstdio>
char s[101];
int main(){
	int test;
	scanf("%d",&test);
	while(test--){
		int n;
		scanf("%d",&n);
		scanf("%s",s+1);
		for(int i=1;i<=n;i++)
			printf("%c",s[i*2-1]);
		printf("\n");
	}
}

B:

题意:

你是一个人,你还有一个随从(

你的能力值为 \(p\),你的随从的能力值为 \(f\)

两种武器,都有一定的数量和重量。

最多能拿多少武器。

brute force

#include<cstdio>
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
int main(){
	int test;
	scanf("%d",&test);
	while(test--){
		int a,b,c,d,e,f;
		scanf("%d%d%d%d%d%d",&a,&b,&c,&d,&e,&f);
		if(e>f){
			e^=f^=e^=f;
			c^=d^=c^=d;
		}
		int ans=0;
		for(int i=0;i<=c&&1ll*e*i<=a;i++){
			int j=min(d,(a-e*i)/f);
			int rest_c=c-i;
			int rest_d=d-j;
			int x=min(rest_c,b/e);
			int y=min(rest_d,(b-e*x)/f);
			ans=max(ans,i+j+x+y);
		}
		printf("%d\n",ans);
	}
}

C:

题意:

给你一个字符串 \(s\)。要构造一个 \(w\) 使得通过给定的操作使它变成 \(s\)
不能就 "-1"。

操作方式是 \(s_i = w_{i-x}|w_{i+x}\)

简单构造,考虑全部变成 0,能填就填,不能填就 -1。

#include<bits/stdc++.h>
using namespace std;
int main(){
	int test;
	cin>>test;
	while(test--){
		string s;
		cin>>s;
		int x;
		cin>>x;
		int n=s.length();
		string answer(n,'0');
		for(int i=0;i<n;i++){
			if(s[i]=='0'){
				if(i-x>=0&&answer[i-x]=='1'){
					answer="-1";
					break;
				}
			} else {
				if(i-x>=0&&answer[i-x]=='1'){
					continue;
				}
				if(i-x>=0&&(i<2*x||s[i-2*x]=='1')){
					answer[i-x]='1';
				} else if(i+x>=n) {
					answer="-1";
					break;
				} else {
					answer[i+x]='1';
				}
			}
		}
		cout<<answer<<'\n';
	}
}

D:

题意:

求多少对 \(1\leq i<j<k<l\leq n,a_i=a_k,a_j=a_l\)

首先我们考虑一个前缀和之类的东西。

枚举 \(k\),然后更新 \(k\) 这个位置带来的贡献,并记录一个 \(cnt\) 数组来存这一对出现了多少次,然后把 \(k\) 往前移动的时候前缀和统计一下后面的状态,然后再令 \(j=k-1\),枚举一个 \(i\),加一下即可。

但是发现这个前缀和是没有必要的,可以直接做。


#include<bits/stdc++.h>
using namespace std;
int a[3001];
const int MAX=3001*3001;
int cnt[MAX];

int main(){
	int test;
	cin>>test;
	while(test--){
		int n;
		cin>>n;
		for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)cnt[i*n+j]=0;
		for(int i=1;i<=n;i++)cin>>a[i];
		long long answer=0;
		for(int k=n;k;--k){
			for(int l=k+1;l<=n;l++)
				cnt[a[k]*n+a[l]]++;
			int j=k-1;
			for(int i=1;i<j;i++)
				answer+=cnt[a[i]*n+a[j]];
		}
		cout<<answer<<'\n';
	}
}

E:

题意同 448C。

我抄我自己。

考虑暴力分治递归即可。

#include<cstdio>
int n,a[5001];

int min(int x,int y){return x<y?x:y;}
int solve(int l,int r,int lst){
	if(l>r)return 0;
	int mn=1e9;
	for(int i=l;i<=r;i++)mn=mn<a[i]?mn:a[i];
	int pre=l,ans=mn-lst;
	for(int i=l;i<=r;i++)
		if(a[i]==mn){
			ans+=solve(pre,i-1,mn);
			pre=i+1;
		}
	ans+=solve(pre,r,mn);
	return min(ans,r-l+1);
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	printf("%d",solve(1,n,0));
	return 0;
}

F:

题意:

定义 \(f(l,r)\)\(l\)~\(r\) 的数位之和。

我们定义一个子串是 \(x-prime\) 有三个条件。

\(f(l,r) = x\)

没有 \(f(l2,r2)!=x\)

没有 \(x \mid f(l2,r2)\)

\([l\leq l2,r2\leq r]\)

删除最小的字符个数使得给出的字符串 \(s\) 不包含 \(x-prime\) 的子串。

简单题。

\(x\leq 20\) ,就考虑 \(brute\) 出来 \(x-prime\) 的所有合法情况。

然后一个个插到字典树上,AC自动机跑一遍,求出 \(fail\) 之后简单 \(dp\) 即可。

#include<bits/stdc++.h>

const int MAXNODE=10005;
struct acam{
	int tr[MAXNODE][10];
	int fail[MAXNODE];
	bool ed[MAXNODE];
	int _=1;
	
	int newnode(){
		return ++_;
	}
	
	void insert(std::vector<int>a){
		int p=1;
		for(auto i:a){
			int&nxt=tr[p][i];
			if(!nxt)nxt=newnode();
			p=nxt;
		}
		ed[p]=true;
	}
	
	void build(){
		std::queue<int>q;
		for(int i=1;i<10;i++)
			if(tr[1][i])
				fail[tr[1][i]]=1,q.push(tr[1][i]);
			else
				tr[1][i]=1;
		while(!q.empty()){
			int u=q.front();
			q.pop();
			for(int i=1;i<10;i++){
				if(tr[u][i]){
					fail[tr[u][i]]=tr[fail[u]][i];
					ed[i]|=ed[fail[i]];
					q.push(tr[u][i]);
				}else tr[u][i]=tr[fail[u]][i];
			}
		}
	}
}acam;

const int N=1002;
char s[N];
int a[N],x;

void dfs(int s,std::vector<int>t){
	if(s==x){
		int tmp=0;
		int sz=t.size();
		for(int i=0;i<sz;i++){
			tmp=0;
			for(int j=i;j<sz;j++){
				tmp+=t[j];
				if(tmp!=x&&x%tmp==0)return;
			}
		}
		acam.insert(t);
		return;
	}
	for(int i=1;i<10&&s+i<=x;i++){
		t.push_back(i);
		dfs(s+i,t);
		t.pop_back();
	}
}

int dp[N][MAXNODE];

int main(){
	scanf("%s",s+1);
	int n=strlen(s+1);
	for(int i=1;i<=n;i++)a[i]=s[i]^'0';
	scanf("%d",&x);
	dfs(0,{});
	acam.build();
	int allnode=acam._;
	
	memset(dp,63,sizeof(dp));
	dp[1][1]=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=allnode;j++){
			int nxt=acam.tr[j][a[i]];
			if(!acam.ed[nxt])
				dp[i+1][nxt]=std::min(dp[i+1][nxt],dp[i][j]);
			dp[i+1][j]=std::min(dp[i+1][j],dp[i][j]+1);
		}
	}
	
	int answer=1e9;
	for(int i=1;i<=allnode;i++)
		answer=std::min(answer,dp[n+1][i]);
	
	printf("%d\n",answer);
}

G:

题意:

\(n\) 个人,这 \(n\) 个人要求选出来的人数的 range 在 \([l_i,r_i]\)。然后有 \(m\) 对人相互憎恨。(意思是不能同时选)求选出来的方案数个数。对 998244353 取模。

我们考虑差分一下。

rep(i,1,n){int l,r;scanf("%d%d",&l,&r);cnt[L[i]=l]++;cnt[(R[i]=r)+1]--;}rep(i,1,n)add(cnt[i],cnt[i-1]);

这样 \(cnt_i\) 就是选 \(i\) 个人的情况下能选的人数了。

然后考虑组合数一下,显然这个方案数是 \(\binom{i}{cnt_i}\)

我们钦定 \(j\) 个位置,这个方案数就是 \(\binom{i-j}{cnt_i-j}\)

考虑 \(m=0\) 的时候这个答案显然是 \(\sum_{i=1}^{n}\binom{i}{cnt_i}\)

\(m>0\) 的时候呢?

发现有一堆位置是要自己钦定的。

我们定义 \(f_{j,k} = \sum_{i=1}^{k}\binom{i-j}{cnt_i-j}\)

定义一个状态 \(state\) 是选多对关系情况下的方案数,但是这个玩意会算重复。

考虑容斥。

\(\sum_{t}(-1)^{|t|} \binom{|t|}{m}f(t)\)

实际上我们是钦定了有关系的那些人 (要去重) 全部都选的时候的方案数减掉。

#include<cstdio>
#include<set>
#define rep(i,x,y) for(int i=x;i<=y;i++)
int min(int x,int y){return x<y?x:y;}
int max(int x,int y){return x>y?x:y;}
const int N=3e5+2;
int L[N],R[N],fac[N],ifac[N];
const int mod=998244353;
int mul(const int&x,const int&y){return 1ll*x*y%mod;}
void add(int&x,const int&y){x+=y;if(x>=mod)x-=mod;}
void dec(int&x,const int&y){x-=y;if(x<0)x+=mod;}
int qpow(int base,int t){//base^t
	int result=1;while(t){if(t&1)result=mul(result,base);base=mul(base,base);t>>=1;}return result;
}
int C(int x,int y){if(x<y||x<0||y<0)return 0;return mul(fac[x],mul(ifac[x-y],ifac[y]));}
int n,m,cnt[N],f[41][N];
int A[20],B[20];
int main(){
	fac[0]=ifac[0]=1;rep(i,1,N-1)fac[i]=mul(fac[i-1],i),ifac[i]=qpow(fac[i],mod-2);
	scanf("%d%d",&n,&m);
	rep(i,1,n){int l,r;scanf("%d%d",&l,&r);cnt[L[i]=l]++;cnt[(R[i]=r)+1]--;}rep(i,1,n)add(cnt[i],cnt[i-1]);
//	rep(i,1,n)printf("cnt[%d]=%d\n",i,cnt[i]);
	rep(j,0,2*m)rep(i,1,n)f[j][i]=f[j][i-1],add(f[j][i],C(cnt[i]-j,i-j));
	rep(i,0,m-1)scanf("%d%d",&A[i],&B[i]);
	int up=1<<m,answer=0;
	rep(i,0,up-1){
		int l=1,r=n;
		std::set<int>s;
		rep(j,0,m-1)if(i>>j&1)l=max(l,max(L[A[j]],L[B[j]])),r=min(r,min(R[A[j]],R[B[j]])),s.insert(A[j]),s.insert(B[j]);
		if(l>r)continue;
		int sz=(int)s.size();
		int tmp=(f[sz][r]-f[sz][l-1]+mod)%mod;
		if(__builtin_popcount(i)&1)dec(answer,tmp);else add(answer,tmp);
	}
	printf("%d\n",answer);
	return 0;	
}
posted @ 2020-08-26 15:15  _Isaunoya  阅读(212)  评论(1编辑  收藏  举报