Educational Codeforces Round 94 题解

A

由于整个串长度为 2 n − 1 2n-1 2n1,所以每个长度为 n n n 的串都肯定会覆盖到第 n n n 个位置,所以直接令答案字符串全部为为第 n n n 个字符即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100010

int T,n;
char s[maxn];

int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);
		scanf("%s",s+1);
		for(int i=1;i<=n;i++)printf("%c",s[n]);printf("\n");
	}
}

B

枚举第一个人拿多少把剑,那么剩下的拿法就确定了:用斧头塞满第一个人的背包,第二个人尽可能拿重量小的,如果背包还有空就放满另一种武器。

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100010

int T,n,m,ca,cb,va,vb;
char s[maxn];

int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d %d %d %d %d %d",&n,&m,&ca,&cb,&va,&vb);
		int ans=0;
		if(n<m)swap(n,m);
		if(va>vb)swap(ca,cb),swap(va,vb);
		for(int i=0;i<=ca;i++){
    
    
			if(1ll*i*va>1ll*n)break;
			int tot=i,c,d,e;
			c=min((n-i*va)/vb,cb);tot+=c;
			d=min(m/va,ca-i);tot+=d;
			e=min((m-d*va)/vb,cb-c);tot+=e;
			ans=max(ans,tot);
		}
		printf("%d\n",ans);
	}
}

C

容易发现,如果 s i = 0 s_i=0 si=0,那么 w i − x w_{i-x} wix w i + x w_{i+x} wi+x 一定是 0 0 0,然后让 w w w 剩下的位全部放 1 1 1,再去检查一下 s i = 1 s_i=1 si=1 的位,看看 w i + x w_{i+x} wi+x w i − x w_{i-x} wix 有没有 1 1 1 即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100010

int T,n,c;
char s[maxn],ans[maxn];
bool tf;

int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%s",s+1);n=strlen(s+1);
		scanf("%d",&c);tf=false;
		for(int i=1;i<=n;i++)ans[i]='1';
		for(int i=1;i<=n;i++)if(s[i]=='0'){
    
    
			if(i-c>=1)ans[i-c]='0';
			if(i+c<=n)ans[i+c]='0';
		}
		for(int i=1;i<=n;i++)if(s[i]=='1'){
    
    
			int tot=0;
			if(i-c>=1)tot|=(ans[i-c]=='1');
			if(i+c<=n)tot|=(ans[i+c]=='1');
			if(!tot)tf=true;
		}
		if(tf)printf("-1\n");
		else{
    
    
			for(int i=1;i<=n;i++)printf("%c",ans[i]);
			printf("\n");
		}
	}
}

D

枚举 i , k i,k i,k 或枚举 j , l j,l j,l 统计一下就好,我是枚举的 i , k i,k i,k,但是枚举 j , l j,l j,l 会更简单。

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 3010
#define ll long long

int T,n,a[maxn],A[maxn],B[maxn];
ll ans,now;

int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);ans=0;
		for(int i=1;i<=n;i++)scanf("%d",&a[i]);
		for(int i=1;i<=n;i++){
    
    
			now=0;
			memset(A,0,sizeof(A));//A记录(i,j)中数字的出现次数
			memset(B,0,sizeof(B));//B记录(j,n]中数字的出现次数
			for(int j=n;j>=i+1;j--)B[a[j]]++;
			for(int j=i+1;j<=n;j++){
    
    
				B[a[j]]--;now-=A[a[j]];
				if(a[i]==a[j])ans+=now;
				A[a[j]]++;now+=B[a[j]];
			}
		}
		printf("%lld\n",ans);
	}
}

E

对于当前序列,只有两种操作可以选择:找到最小的数 x x x,操作 x x x 次使全部数减 x x x,此时序列被 0 0 0 分成了两部分,递归进去处理即可;操作 序列长度 次使每个数都变成 0 0 0

套个笛卡尔树可以做到 O ( n ) O(n) O(n),但是 n n n 只有 5000 5000 5000 直接暴力即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100010
#define ll long long

int T,n,a[maxn];
int solve(int l,int r){
    
    
	if(l>r)return 0;
	if(l==r)return a[l]>0;
	int mi=1e9,pos,p=0;
	for(int i=l;i<=r;i++){
    
    
		if(a[i]<mi)mi=a[i],pos=i;
		p+=(a[i]>0);
	}
	if(mi>=p)return p;
	for(int i=l;i<=r;i++)a[i]-=mi;
	return min(solve(l,pos-1)+solve(pos+1,r)+mi,p);
}

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

上面是赛时写的,后来膜别人代码得到了更短的写法,但思路是一样的:

#include <cstdio>
#include <algorithm>
using namespace std;

int n,a[5010];
int solve(int l,int r,int mi){
    
    
	if(l>r)return 0;
	int c=min_element(a+l,a+r+1)-a;
	return min(solve(l,c-1,a[c])+solve(c+1,r,a[c])+a[c]-mi,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));
}

F

由于 x x x 很小,所以可以爆搜出所有 x -prime x\text{-prime} x-prime 字符串,然后造一个 AC \text{AC} AC 自动机跑 dp \text{dp} dp 即可,转移的时候枚举每个节点,然后考虑这个字符删不删即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100010

int n,k;
char s[maxn];
struct node{
    
    int ne[10],fail,end;};
node st[maxn];int t=0;
void insert(char *ss,int len){
    
    
	int now=0;
	for(int i=0;i<len;i++){
    
    
		if(!st[now].ne[ss[i]-'0'])st[now].ne[ss[i]-'0']=++t;
		now=st[now].ne[ss[i]-'0'];
	}
	st[now].end=1;
}
int q[maxn],S=1,T=0;
void make_fail(){
    
    
	st[0].fail=-1;
	for(int i=0;i<10;i++)if(st[0].ne[i])q[++T]=st[0].ne[i];
	while(S<=T){
    
    
		int x=q[S++];
		for(int i=0;i<10;i++)if(st[x].ne[i]){
    
    
			int j=st[x].fail;
			while(j!=-1&&!st[j].ne[i])j=st[j].fail;
			if(j!=-1)st[st[x].ne[i]].fail=st[j].ne[i];
			q[++T]=st[x].ne[i];
		}
	}
}
char d[maxn];int dd=0;
bool check(){
    
    
	for(int i=0;i<dd;i++){
    
    
		int sum=0;
		for(int j=i;j<dd;j++){
    
    
			sum+=d[j]-'0';
			if(sum>=k)break;
			if(k%sum==0)return false;
		}
	}
	return true;
}
void dfs(int x){
    
    
	if(!x){
    
    
		if(check())insert(d,dd);
		return;
	}
	for(int i=1;i<=min(x,9);i++){
    
    
		d[dd++]=i+'0';
		dfs(x-i);
		dd--;
	}
}
int f[maxn],g[maxn];
void chkmin(int &x,int y){
    
    if(y<x)x=y;}

int main()
{
    
    
	scanf("%s",s);n=strlen(s);
	scanf("%d",&k);dfs(k);make_fail();
	memset(f,63,sizeof(f));f[0]=0;
	for(int i=0;i<n;i++){
    
    
		for(int j=0;j<=t;j++)g[j]=n;
		for(int j=0;j<=t;j++){
    
    
			chkmin(g[j],f[j]+1);//删
			int now=j;
			while(now&&!st[now].ne[s[i]-'0'])now=st[now].fail;
			if(st[now].ne[s[i]-'0'])now=st[now].ne[s[i]-'0'];
			if(!st[now].end)chkmin(g[now],f[j]);//不删
		}
		swap(f,g);
	}
	int ans=n;
	for(int i=0;i<=t;i++)chkmin(ans,f[i]);
	printf("%d",ans);
}

G

考虑没有相互讨厌这个限制时怎么做:令 c i c_i ci 表示有多少个人的区间 [ l i , r i ] [l_i,r_i] [li,ri] 包含 i i i,答案就是 ∑ i = 1 n C c i i \sum_{i=1}^n C_{c_i}^i i=1nCcii

有了相互讨厌的限制也不难做,考虑容斥即可,假设此时至少包含有 k k k 对相互讨厌的人,这 k k k 对中不同的人有 d d d 个,那么贡献就是 ( − 1 ) k ∑ i = 1 n C c i − d i − d (-1)^k\sum_{i=1}^n C_{c_i-d}^{i-d} (1)ki=1nCcidid

由于 d ≤ 40 d\leq 40 d40,所以可以 O ( 40 n ) O(40n) O(40n) 预处理,容斥时 O ( 1 ) O(1) O(1) 求贡献。(赛时脑子抽了,没发现范围很小可以预处理,当时直接暴力容斥居然还过了 73 73 73 个点qwq)

还有一些细节就看代码吧:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 300010
#define mod 998244353

int T,n,m,l[maxn],r[maxn],a[maxn],b[maxn];
int ksm(int x,int y){
    
    int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
int fac[maxn],inv_fac[maxn];
void work(){
    
    
	fac[0]=inv_fac[0]=1;
	for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod;
	inv_fac[n]=ksm(fac[n],mod-2);
	for(int i=n-1;i>=1;i--)inv_fac[i]=1ll*inv_fac[i+1]*(i+1)%mod;
}
int C(int x,int y){
    
    return 1ll*fac[x]*inv_fac[y]%mod*inv_fac[x-y]%mod;}
int s[maxn],sum[maxn][41],ans;
void add(int &x,int y){
    
    x=(x+y>=mod?x+y-mod:x+y);}
void dec(int &x,int y){
    
    x=(x-y<0?x-y+mod:x-y);}
bool v[maxn];

int main()
{
    
    
	scanf("%d %d",&n,&m);work();
	for(int i=1;i<=n;i++)scanf("%d %d",&l[i],&r[i]),s[l[i]]++,s[r[i]+1]--;
	for(int i=1;i<=n;i++)s[i]+=s[i-1];
	for(int k=0;k<=40;k++){
    
    
		for(int i=1;i<=n;i++){
    
    
			sum[i][k]=sum[i-1][k];
			if(i>=k&&s[i]>=i)add(sum[i][k],C(s[i]-k,i-k));
		}
	}
	ans=sum[n][0];
	for(int i=1;i<=m;i++)scanf("%d %d",&a[i],&b[i]);
	for(int i=1;i<(1<<m);i++){
    
    
		int L=0,R=1e9,p=1,tot=0;
		for(int j=0;j<m;j++)if(i>>j&1){
    
    
			L=max(L,max(l[a[j+1]],l[b[j+1]]));
			R=min(R,min(r[a[j+1]],r[b[j+1]]));
			if(!v[a[j+1]])v[a[j+1]]=true,tot++;
			if(!v[b[j+1]])v[b[j+1]]=true,tot++;
			p*=-1;
		}
		L=max(L,tot);if(L<=R){
    
    
			if(p==1)add(ans,(sum[R][tot]-sum[L-1][tot]+mod)%mod);
			else dec(ans,(sum[R][tot]-sum[L-1][tot]+mod)%mod);
		}
		for(int j=0;j<m;j++)if(i>>j&1){
    
    
			v[a[j+1]]=false;
			v[b[j+1]]=false;
		}
	}
	printf("%d",ans);
}

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/108255270
今日推荐