Educational Codeforces Round 64(Unrated for Div.1+Div. 2)

什么垃圾比赛,A题说的什么鬼楞是没看懂。就我只会BD(其实C是个大水题二分),垃圾游戏,技不如人,肝败吓疯,告辞,口胡了E就睡觉了。

B

很容易发现,存在一种方案,使得相同字母连在一起,然后发现,当字母出现种类数大于等于4时,可以奇偶性相间地连接,然后讨论种类数<=3的:种类数为1,显然直接输出;种类数为2,若两字母相邻则无解,否则直接输出;种类数为3,若三字母相邻则无解,否则按照213/231(至少一种符合条件)输出。

#include<bits/stdc++.h>
using namespace std;
const int N=107;
int T,n,m,ans,sum[N],id[N];
char s[N];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",s+1),n=strlen(s+1);
        memset(sum,0,sizeof sum);
        memset(id,0,sizeof id);
        for(int i=1;i<=n;i++)sum[s[i]-'a'+1]++;
        int num=0;
        for(int i=1;i<=26;i++)if(sum[i])id[i]=++num;
        if(num==1)
        {
            for(int i=1;i<=n;i++)printf("%c",s[i]);
        }
        else if(num>=4)
        {
            for(int i=26;i>=1;i--)
            if(id[i]&1)
            {
                for(int j=1;j<=sum[i];j++)printf("%c",'a'+i-1);
            }
            for(int i=26;i>=1;i--)
            if(id[i]&&id[i]%2==0)
            {
                for(int j=1;j<=sum[i];j++)printf("%c",'a'+i-1);
            }
        }
        else if(num==3)
        {
            int flag=0;
            for(int i=2;i<=25;i++)if(sum[i]&&sum[i-1]&&sum[i+1])flag=1;
            if(flag)printf("No answer");
            else{
                for(int i=2;i<=25;i++)
                for(int j=1;j<i;j++)
                for(int k=i+1;k<=26;k++)
                if(sum[j]&&sum[i]&&sum[k])
                {
                    for(int t=1;t<=sum[i];t++)printf("%c",'a'+i-1);
                    if(j==i-1)
                    {
                        for(int t=1;t<=sum[k];t++)printf("%c",'a'+k-1);
                        for(int t=1;t<=sum[j];t++)printf("%c",'a'+j-1);
                    }
                    else{
                        for(int t=1;t<=sum[j];t++)printf("%c",'a'+j-1);
                        for(int t=1;t<=sum[k];t++)printf("%c",'a'+k-1);
                    }
                }
            }
        }
        else{
            int flag=0;
            for(int i=1;i<=25;i++)if(sum[i]&&sum[i+1])flag=1;
            if(flag)printf("No answer");
            else{
                for(int i=1;i<=26;i++)if(sum[i])
                {
                    for(int j=1;j<=sum[i];j++)printf("%c",'a'+i-1);
                }
            }
        }
        puts("");
    }
}
View Code

D

很容易想到一个DP,令f[i]表示以i为根的子树,从下面的节点走上来,最后一步是黑边的点数,g[i]表示全走白边的点数,于是就有f[u]=Σ(f[son]+g[son]+1),son为经过黑边的son,g[u]=Σ(g[son]+1),son为经过白边的son。然后这个东西很容易换根DP,根据黑白边讨论一下即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+7;
int n,cnt,f[N],g[N],nf[N],ng[N],hd[N],v[N<<1],nxt[N<<1],w[N<<1];
ll ans;
void adde(int x,int y,int z){v[++cnt]=y,nxt[cnt]=hd[x],w[cnt]=z,hd[x]=cnt;}
void dfs(int u,int fa)
{
    for(int i=hd[u];i;i=nxt[i])
    if(v[i]!=fa)
    {
        dfs(v[i],u);
        if(!w[i])g[u]+=g[v[i]]+1;
        else f[u]+=f[v[i]]+g[v[i]]+1;
    }
}
void dfs2(int u,int fa)
{
    ans+=nf[u]+ng[u];
    for(int i=hd[u];i;i=nxt[i])
    if(v[i]!=fa)
    {
        if(!w[i])nf[v[i]]=f[v[i]],ng[v[i]]=ng[u];
        else nf[v[i]]=nf[u]-g[v[i]]+ng[u],ng[v[i]]=g[v[i]];
        dfs2(v[i],u);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1,x,y,z;i<n;i++)scanf("%d%d%d",&x,&y,&z),adde(x,y,z),adde(y,x,z);
    dfs(1,0);
    nf[1]=f[1],ng[1]=g[1],dfs2(1,0);
    cout<<ans;
}
View Code

E

口胡了一个分治做法,一写发现,它居然过了。感觉本题比D简单。

做法大致如下:直接算很难处理,考虑分治,对于长度大于等于3的区间[l,r],考虑覆盖mid和mid+1的所有区间,可以把[l,r]分为[l,mid]和[mid+1,r]两半,然后mx[i]对于左半部分表示后缀最大值,对于右半部分表示前缀最大值,然后枚举位置计算另一端的位置是否符合题意,因为区间的max值出现在两端的mx之一,所以左右都搜一下即可统计所有答案。复杂度O(nlogn)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+7;
int n,ans,a[N],b[N],mx[N];
void solve(int l,int r)
{
    if(l+1>=r)return;
    int mid=l+r>>1;
    solve(l,mid),solve(mid+1,r);
    mx[mid]=a[mid];for(int i=mid-1;i>=l;i--)mx[i]=max(mx[i+1],a[i]);
    mx[mid+1]=a[mid+1];for(int i=mid+2;i<=r;i++)mx[i]=max(mx[i-1],a[i]);
    for(int i=l;i<=mid;i++)
    {
        int pos=b[mx[i]-a[i]];
        if(pos>mid&&pos<=r&&mx[pos]<mx[i])ans++;
    }
    for(int i=mid+1;i<=r;i++)
    {
        int pos=b[mx[i]-a[i]];
        if(pos>=l&&pos<=mid&&mx[pos]<mx[i])ans++;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[a[i]]=i;
    solve(1,n);
    printf("%d",ans);
}
View Code

F

很容易想到dp,令f[i][j]表示第i轮当前卡为j且游戏继续的概率,然后根据第i轮每张卡有1/(n-i+1)的概率选中,直接写个前缀和,根据题意暴力DP转移即可,代码20行。

感觉这题更简单,CF这场什么垃圾排题顺序,难怪Unrated

#include<bits/stdc++.h>
using namespace std;
const int N=5005,mod=998244353;
int n,ans,a[N],s[N],inv[N],f[N][N];
int main()
{
    scanf("%d",&n);
    for(int i=1,x;i<=n;i++)scanf("%d",&x),a[x]++;
    for(int i=n;i;i--)s[i]=s[i+1]+a[i];
    inv[1]=1;for(int i=2;i<=n;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=1;i<=n;i++)f[1][i]=1ll*a[i]*inv[n]%mod;
    for(int i=2;i<=n;i++)
    for(int j=1,sum=0;j<=n;j++)
    f[i][j]=1ll*a[j]*sum%mod,sum=(sum+1ll*f[i-1][j]*inv[n-i+1])%mod;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    if(a[j]>1)ans=(ans+1ll*f[i][j]%mod*(a[j]-1)%mod*inv[n-i])%mod;
    printf("%d",ans);
}
View Code

G

看了下是个没有意思的大模拟,不想写也不会写,咕了。

感觉真实难度顺序:C<B<E<D=F<G<A

posted @ 2019-05-01 23:49  hfctf0210  阅读(229)  评论(0)    收藏  举报