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;
}