Educational Codeforces Round 70 题解

噩梦场。

题目出奇的难,好像一群外国老哥看 A 看着看着就哭了……


A

找到 \(b\) 最低的 \(1\),这个 \(1\) 肯定要跟 A 中的一个 \(1\) 搭配,而且是能搭配的 \(1\) 中最低的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=100010,mod=998244353;
#define MP make_pair
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
int t,n,m;
char a[maxn],b[maxn];
int main(){
	t=read();
	while(t--){
		scanf("%s",a+1);scanf("%s",b+1);
		n=strlen(a+1);m=strlen(b+1);
		int at,ans=0;
		ROF(i,m,1) if(b[i]=='1'){at=n-(m-i);break;}
		while(at>0 && a[at]=='0') at--,ans++;
		printf("%d\n",ans);
	}
}

B

大力枚举 \(i,j\)。对于每个 \(i,j\)\(O(n)\) 算,每次就是问在相邻两个数之间最少加多少个数。特别注意相邻两个数相同的情况。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=2000200,mod=998244353;
#define MP make_pair
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
int n,ok[10][10][111],okk[10][10][10];
char s[maxn];
int main(){
	scanf("%s",s+1);
	n=strlen(s+1);
	MEM(ok,0x3f);MEM(okk,0x3f);
	FOR(i,0,9) FOR(j,0,9){
		ok[i][j][0]=0;
		FOR(l,0,99){
			ok[i][j][l+i]=min(ok[i][j][l+i],ok[i][j][l]+1);
			ok[i][j][l+j]=min(ok[i][j][l+j],ok[i][j][l]+1);
		}
		FOR(k,1,110) okk[i][j][k%10]=min(okk[i][j][k%10],ok[i][j][k]);
		if(!i || !j) okk[i][j][0]=1;
//		FOR(k,0,9) printf("ok[%d][%d][%d]=%d\n",i,j,k,ok[i][j][k]);
	}
	FOR(i,0,9){
		FOR(j,0,9){
			int ans=0;
			bool flag=true;
			FOR(k,2,n){
				int x=okk[i][j][(s[k]-s[k-1]+10)%10];
				x=max(x-1,0);
				if(x>=1e9){printf("-1 ");flag=false;break;}
				ans+=x;
			} 
			if(flag) printf("%d ",ans);
		}
		puts("");
	}
}

C

毒瘤玩意……当然可能是我写复杂了。

上下和左右互不干扰,分开考虑。以上下为例。

把上看成 \(1\),下看成 \(-1\),那么竖直方向一共跨过了最大前缀和-最小前缀和单位。

不妨枚举在哪里插入字符,然后瞎合并一通。

需要很多东西,比如每个前缀的后缀和的后缀最大值。(smg……)

不保证代码能让大家都理解。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=200020,mod=998244353;
#define MP make_pair
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
int t,n,n1,n2,pre1[maxn],suf1[maxn],pre2[maxn],suf2[maxn];
int mnpre1[maxn],mxpre1[maxn],mnpre2[maxn],mxpre2[maxn],mnsuf1[maxn],mxsuf1[maxn],mnsuf2[maxn],mxsuf2[maxn];
int s1[maxn],s2[maxn];
ll ans;
char s[maxn];
void calc(int n,int a[],int pre[],int suf[],int mnpre[],int mxpre[],int mnsuf[],int mxsuf[]){
	FOR(i,1,n) pre[i]=pre[i-1]+a[i];
	ROF(i,n,1) suf[i]=suf[i+1]+a[i];
	FOR(i,1,n) mnpre[i]=a[i]+min(0,mnpre[i-1]),mxpre[i]=a[i]+max(0,mxpre[i-1]);
	ROF(i,n,1) mnsuf[i]=min(mnsuf[i+1],suf[i]),mxsuf[i]=max(mxsuf[i+1],suf[i]);
//	FOR(i,1,n) printf("pre[%d]=%d,suf[%d]=%d,mnpre[%d]=%d,mxpre[%d]=%d,mnsuf[%d]=%d,mxsuf[%d]=%d\n",i,pre[i],i,suf[i],i,mnpre[i],i,mxpre[i],i,mnsuf[i],i,mxsuf[i]);
}
int main(){
	t=read();
	while(t--){
		scanf("%s",s+1);
		n=strlen(s+1);
		n1=n2=0;
		FOR(i,1,n){
			if(s[i]=='W') s1[++n1]=1;
			else if(s[i]=='S') s1[++n1]=-1;
			else if(s[i]=='A') s2[++n2]=1;
			else s2[++n2]=-1;
		}
		calc(n1,s1,pre1,suf1,mnpre1,mxpre1,mnsuf1,mxsuf1);
		calc(n2,s2,pre2,suf2,mnpre2,mxpre2,mnsuf2,mxsuf2);
		ans=1ll*(mxsuf2[1]-mnsuf2[1]+1)*(mxsuf1[1]-mnsuf1[1]+1);
//		cout<<ans<<endl;
		FOR(i,0,n1) ans=min(ans,1ll*(mxsuf2[1]-mnsuf2[1]+1)*(
		min(
		max(mxsuf1[i+1],suf1[i+1]+1+max(mxpre1[i],0))-min(mnsuf1[i+1],suf1[i+1]+1+min(mnpre1[i],0)),
		max(mxsuf1[i+1],suf1[i+1]-1+max(mxpre1[i],0))-min(mnsuf1[i+1],suf1[i+1]-1+min(mnpre1[i],0))
		)+1));
//		cout<<ans<<endl;
		FOR(i,0,n2) ans=min(ans,1ll*(mxsuf1[1]-mnsuf1[1]+1)*(
		min(
		max(mxsuf2[i+1],suf2[i+1]+1+max(mxpre2[i],0))-min(mnsuf2[i+1],suf2[i+1]+1+min(mnpre2[i],0)),
		max(mxsuf2[i+1],suf2[i+1]-1+max(mxpre2[i],0))-min(mnsuf2[i+1],suf2[i+1]-1+min(mnpre2[i],0))
		)+1));
		cout<<ans<<endl;
		FOR(i,0,n+1) pre1[i]=suf1[i]=pre2[i]=suf2[i]=mxpre1[i]=mnpre1[i]=mxsuf1[i]=mnsuf1[i]=mxpre2[i]=mnpre2[i]=mxsuf2[i]=mnsuf2[i]=0;
	}
}

D

考虑只有一个 \(7\) 能不能做。\(7\) 明显在最右边。对于每个 \(1\),求出它右边有 \(x_i\)\(3\),答案就是 \(\sum\frac{x_i(x_i-1)}{2}\)

然后每次选一个尽可能大的 \(x_i\),不停构造,由于 \(x_i\) 肯定不超过 \(50000\),而且无论 \(n\) 多小都可以在后面加 \(2\)\(3\) 使得 \(n\) 减少 \(1\),所以这组解一定存在且合法。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=100010,mod=998244353;
#define MP make_pair
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
int t,n,a[maxn],k;
int main(){
	t=read();
	while(t--){
		n=read();k=0;MEM(a,0); 
		while(n){
			a[++k]=sqrt(2*n);
			while(a[k]*(a[k]-1)<=2*n) a[k]++;
			while(a[k]*(a[k]-1)>2*n) a[k]--;
			n-=a[k]*(a[k]-1)/2;
		}
		FOR(i,1,k){
			printf("1");
			FOR(j,1,a[i]-a[i+1]) printf("3");
		}
		printf("7\n");
	}
}

E

最小清新的一道题。

考虑求出 \(a[i]\) 表示在 \(t\) 中能以 \(i\) 结尾匹配的串的个数,\(b[i]\) 表示在 \(t\) 中能以 \(i\) 开头匹配的串的个数。答案是 \(\sum a[i]b[i+1]\)

这两个东西都可以通过 AC 自动机简单求。大概就是维护 fail 链上末尾节点的个数之类的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=200020,mod=998244353;
#define MP make_pair
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
struct ACAM{
	int cnt,ch[maxn][26],fail[maxn],q[maxn],sum[maxn],h,r,val[maxn];
	void insert(char *s,int l){
		int now=0;
		FOR(i,1,l){
			int p=s[i]-'a';
			if(!ch[now][p]) ch[now][p]=++cnt;
			now=ch[now][p];
		}
		sum[now]++;
	}
	void build(){
		h=1;r=0;
		FOR(i,0,25) if(ch[0][i]) q[++r]=ch[0][i];
		while(h<=r){
			int u=q[h++];
			FOR(i,0,25) if(ch[u][i]){
				fail[ch[u][i]]=ch[fail[u]][i];
				sum[ch[u][i]]+=sum[fail[ch[u][i]]];
				q[++r]=ch[u][i];
			}
			else ch[u][i]=ch[fail[u]][i];
		}
	}
	void run(char *s,int l){
		int now=0;
		FOR(i,1,l){
			int p=s[i]-'a';
			now=ch[now][p];
			val[i]=sum[now];
		}
	}
}AC[2];
int n,l;
ll ans;
char t[maxn],s[maxn];
int main(){
	scanf("%s",t+1);l=strlen(t+1);
	n=read();
	FOR(i,1,n){
		scanf("%s",s+1);
		int len=strlen(s+1);
		AC[0].insert(s,len);
		for(int j=1,k=len;j<k;j++,k--) swap(s[j],s[k]);
		AC[1].insert(s,len);
	}
	AC[0].build();AC[1].build();
	AC[0].run(t,l);
	for(int i=1,j=l;i<j;i++,j--) swap(t[i],t[j]);
	AC[1].run(t,l);
	FOR(i,1,l) ans+=1ll*AC[0].val[i]*AC[1].val[l-i];
//	FOR(i,1,l) printf("val1[%d]=%d,val2[%d]=%d\n",i,AC[0].val[i],i,AC[1].val[l-i+1]);
	cout<<ans<<endl;
}
posted @ 2019-08-08 22:15  ATS_nantf  阅读(265)  评论(0编辑  收藏  举报