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

转载于:https://www.cnblogs.com/1000Suns/p/11324211.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值