zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 64(ECR64)

    Educational Codeforces Round 64

    CodeForces 1156A

    题意:1代表圆,2代表正三角形,3代表正方形。给一个只含1,2,3的数列a,ai+1内接在ai内,求总共有多少个交点。

    交了好多遍才过。分类讨论一下内接的情况,然后注意到当正方形内接圆形再内接三角形时会有一个点重复,减去即可。

    code

    #include<cstdio>
    int n,fig[105],ans,flag,haha[5][5];
    int main()
    {
        scanf("%d",&n);
        haha[1][2]=3;haha[1][3]=4;
        haha[2][1]=3;haha[2][3]=999999;
        haha[3][1]=4;haha[3][2]=999999;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&fig[i]);
            ans+=haha[fig[i-1]][fig[i]];
            if(fig[i]==2&&fig[i-1]==1&&fig[i-2]==3)ans--;
            if(ans>=999999)
            {
                printf("Infinite
    ");
                return 0;
            }
        }
        printf("Finite
    ");
        printf("%d",ans);
    }

    CodeForces 1156B

    题意:给一个字符串,要求重新排列后不能有字母表中相邻的字母被放在一起(如“ab”与“ba”就不合法)。若可以做到就输出字符串,反之输出“No answer”。

    相同的字母可以缩成一个。发现只有当给的字母是两个或三个相邻的字母时是无法做到的(“ab”,“abc”),特判处理。对所有的字母排序后就可以乱搞了。

    当总共有奇数个字母时,将a(1+n)/2拿出来放到答案中的第一个,剩下的就按a1,an,a2,an-1......这样来排。

    (总共有3个字母时可能第一个或最后一个与中间的那个相邻,特判处理一下)

    当总共有偶数个字母时,将a(1+n)/2+1放到答案中的第一个,a1放到答案中的第二个,an放到答案的第三个,a(1+n)/2放到答案的第四个,剩下的就按an-1,a2,an-2,a3....这样来排。

    (一开始还想用哈密顿回路来做)

    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int T,cha[26],tot,tim[26],ans[26];
    char ch[105];
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%s",ch+1);
            int len=strlen(ch+1);
            for(int i=1;i<=len;i++)
            {
                if(!tim[ch[i]-'a'])
                    cha[++tot]=ch[i]-'a';
                tim[ch[i]-'a']++;
            }
            sort(cha+1,cha+1+tot);
            if((tot==3&&cha[1]+1==cha[2]&&cha[2]+1==cha[3])||(tot==2&&cha[1]+1==cha[2]))
                printf("No answer
    ");
            else
            {
                if(tot%2==0)
                {
                    int mid1=tot/2,mid2=mid1+1;
                    ans[1]=cha[mid2];ans[2]=cha[1];
                    ans[3]=cha[tot];ans[4]=cha[mid1];
                    int l=4;
                    for(int i=2;i<mid1;i++)
                        ans[++l]=cha[tot-i+1],ans[++l]=cha[i];
                }
                else
                {
                    ans[1]=cha[tot/2+1];
                    if(tot!=3||(tot==3&&cha[1]+1!=cha[2]&&cha[2]+1!=cha[3]))
                        for(int i=1;i<=tot/2;i++)
                            ans[i*2]=cha[i],ans[i*2+1]=cha[tot-i+1];
                    else
                    {
                        if(cha[1]+1==cha[2])
                            ans[2]=cha[3],ans[3]=cha[1];
                        else
                            ans[2]=cha[1],ans[3]=cha[3];
                    }
                }
                for(int i=1;i<=tot;i++)
                    for(int j=1;j<=tim[ans[i]];j++)
                        printf("%c",ans[i]+'a');
                printf("
    ");
            }
            memset(cha,0,sizeof cha);tot=0;
            memset(ch,0,sizeof ch);
            memset(tim,0,sizeof tim);
        }
    return 0;
    }

    CodeForces 1156C

    题意:给一串数列a与数字z,若|ai-aj|>=z,则ai与aj可以匹配。一个数只能匹配一次,求最大匹配数。

    本能地给数列排序后,发现新数列有这样一个性质:若有两个匹配之间跨越范围不相交,那么我们可以让它们变成相交的,这样不会影响它们对答案的贡献。但这个性质反过来不一定成立,所以我们匹配时最好让它们相交。

    把数列的中点取出,把中点右边的点当做匹配的右端点,左边的当做左端点,依次匹配就好了。这样做可以让所有匹配跨越范围都经过中点,即在中点相交。

    论证一下严谨性。如果有一边的点能形成不过中点的匹配,那么它肯定能通过另一边的某个点变为过中点的匹配(因为取的是中点,两边点的个数最多相差1,所以另一边肯定存在一个点能保证形成新的匹配)。

    code

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,z,num[200005],ans,vis[200005];
    int main()
    {
        scanf("%d%d",&n,&z);
        for(int i=1;i<=n;i++)
            scanf("%d",&num[i]);
        sort(num+1,num+1+n);
        for(int i=(1+n)/2+1,j=1;i<=n;i++)
        {
            while(vis[j])j++;
            if(!vis[i]&&!vis[j]&&num[i]-num[j]>=z)
                vis[i]=vis[j]=1,ans++,j++;
        }
        printf("%d
    ",ans);
    }

     CodeForces 1156D

    题意:给一棵树,边的权值只有0和1。数对(x,y),x到y的路径上经过权值为0的边后就再也没有经过权值为1的边,求满足条件的数对的个数。

    用并查集求出所有边权只含0和1的连通块,设每个连通块的点的个数为siz。然后分类讨论,只含权值为1或0的边的路径,则每个连通块的贡献为siz*(siz-1)。含权值为0和权值为1的边的路径,枚举路径上01转变的点,找到包含它的只含0的连通块i与只含1的连通块j,产生的贡献为(sizi-1)*(sizj-1)。

    (不需要担心两个联通块还有其它公共点,因为这是棵树)

    看到zzwy大佬还有一种巧妙的算法,直接枚举1.0转变的点,找到包含它的只含0的连通块i与只含1的连通块j,产生的贡献为sizi*sizj-1,这样还同时枚举了边权只含0或1的情况,减去1是去掉自己到自己。

    code

    #include<cstdio>
    int ori[200005][5],n,tim[200005][2];
    long long ans=0;
    int find(int x,int k){return !ori[x][k]?x:ori[x][k]=find(ori[x][k],k);}
    int main()
    {
        scanf("%d",&n);
        for(int i=1,a,b,c;i<=n-1;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            int t1=find(a,c),t2=find(b,c);
            if(t1!=t2)
                ori[t1][c]=t2;
        }
        for(int i=1;i<=n;i++)
            tim[find(i,1)][1]++,tim[find(i,0)][0]++;
        for(int i=1;i<=n;i++)
            ans+=1ll*(tim[find(i,1)][1])*(tim[find(i,0)][0])-1;
        printf("%lld
    ",ans);
    }

     CodeForces 1156E

    题意:给一个数列p,求所有的数对(l,r)的个数,满足pl+pr=maxri=lpi。

    这题抄的题解和代码:

    对于每个数a,我们可以用单调队列求出它左边第一个和右边第一个比它大的数al与ar,那么中间的这段区间(l,r)它就是最大值,之后就暴力枚举就好了。通过类似启发式合并的思想(反正我还没学)可以证明复杂度为nlogn。

    code

    #include<cstdio>
    #define maxn 200005
    int q[maxn],be=1,en,n,p[maxn],pos[maxn],L[maxn],R[maxn];
    long long ans;
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&p[i]),pos[p[i]]=i;p[n+1]=200005;p[0]=200005;
        for(int i=0;i<=n+1;i++)
        {for(;be<=en&&p[q[en]]<p[i];en--);L[i]=q[en];q[++en]=i;}
        for(int i=n+1;i>=0;i--)
        {for(;be<=en&&p[q[en]]<p[i];en--);R[i]=q[en];q[++en]=i;}
        for(int i=1;i<=n;i++)
        {
            if(i-L[i]<R[i]-i)
                for(int j=L[i]+1;j<i;j++)
                {    if(R[i]>pos[p[i]-p[j]]&&pos[p[i]-p[j]]>i)ans++;}
            else
                for(int j=R[i]-1;i<j;j--)
                {    if(L[i]<pos[p[i]-p[j]]&&pos[p[i]-p[j]]<i)ans++;}
        }
        printf("%I64d
    ",ans);
    }

     CodeForces 1156F

    题意:给一堆牌,随便拿,若这次的值小于上次则失败,等于上次的值则成功,大于上次的值则继续游戏,求胜利的期望值。

    第一道自己写的概率dpQAQ

    首先对于任何一个不失败的状态,摸到牌形成的序列应该是不下降的,所以先排个序。我们可以假设dp[i][j]表示第i张牌摸到了值为j的牌且游戏未结束的期望值。因为序列不下降且游戏尚未结束,那么值为j的牌就只用了一张。则dp[i][j]可以由dp[i+1][k](k>j)转移过来。首先,先预处理出每个数字的个数cnt,然后就可以进行转移了。dp[i][j]的期望值应该是 (cntj-1)/n-i 加上(∑dp[i+1][k])/(n-i)(k>j),即下一次抽到j的期望值加上之后获胜的期望值,再用前缀和优化一下就好了。

    code

    #include<cstdio>
    #include<algorithm>
    #define maxn 5005
    #define mod 998244353
    using namespace std;
    int n,a[maxn],inv[maxn],num[maxn],cnt[maxn],f[maxn][maxn],flag=1;
    int main()
    {
        scanf("%d",&n);
        inv[1]=1;for(int i=2;i<=n;i++)inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        sort(a+1,a+1+n);
        for(int i=1;i<=n;cnt[a[i]]++,i++)
            if(!cnt[a[i]])num[++num[0]]=a[i];
        sort(num+1,num+1+num[0]);
        for(int i=n;i>=1;i--)
        {
            long long  sum=0;
            for(int k=1;k<=num[0];k++)
                sum=(sum+1ll*f[flag^1][k]*cnt[num[k]]%mod*inv[n-i]%mod)%mod;
            for(int j=1;j<=num[0];j++)
            {
                sum=(sum-1ll*f[flag^1][j]*cnt[num[j]]%mod*inv[n-i]%mod+mod)%mod;
                f[flag][j]=(1ll*(cnt[num[j]]-1)*inv[n-i]%mod+sum)%mod;
            }
            flag^=1;
        }
        int ans=0;
        for(int i=1;i<=num[0];i++)
            ans=(ans+1ll*f[flag^1][i]*cnt[num[i]]%mod*inv[n]%mod)%mod;
        printf("%d
    ",ans);
    }

     CodeForces 1156G

    恕在下不肖,真的不会。

  • 相关阅读:
    remove white space from read
    optimize the access speed of django website
    dowload image from requests
    run jupyter from command
    crawl wechat page
    python version 2.7 required which was not found in the registry windows 7
    health
    alternate rows shading using conditional formatting
    word
    【JAVA基础】static 关键字
  • 原文地址:https://www.cnblogs.com/firecrazy/p/10822328.html
Copyright © 2011-2022 走看看