Codeforces Hello 2019

Posted winniechen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Hello 2019相关的知识,希望对你有一定的参考价值。

Hello 2019

  • 手速场qwq
  • 反正EGH太神仙了啊.jpg
  • 考试的时候不会啊.jpg

A

暴力.jpg

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
char opt[10],s[10];
int main()
{
    scanf("%s",s+1);
    for(int i=1;i<=5;i++)
    {
        scanf("%s",opt+1);
        if(opt[1]==s[1]||opt[2]==s[2])return puts("YES"),0;
    }
    puts("NO");
    return 0;
}

B

好像也是傻逼题吧?

感觉题意好难理解啊...

然后直接DP.jpg

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 16
int a[N],now,ans,n;
void dfs(int dep,int now)
{
    if(dep==n+1){if(!now)ans=1;return ;}
    dfs(dep+1,(now+a[dep])%360),dfs(dep+1,(now-a[dep]+360)%360);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);dfs(1,0);
    puts(ans?"YES":"NO");
}

C

维护俩信息就好了

$a[i][0],a[i][1]$分别表示左侧多了$i$个左括号,右侧多了$i$个右括号的方案数...

然后就完事了.jpg

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 500005
#define ll long long
char s[N];
int n,a[N][2],tmp;long long ans;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);int len=strlen(s+1),now=0,hav=0;
        for(int j=1;j<=len;j++)
        {
            if(s[j]=='(')now++;
            else
            {
                if(!now)hav++;
                else now--;
            }
        }
        if(hav&&now)continue;
        if(now)a[now][0]++;
        else if(hav)a[hav][1]++;
        else tmp++;
    }
    ans=tmp>>1;
    for(int i=1;i<=500000;i++)ans+=min(a[i][0],a[i][1]);
    printf("%lld
",ans);
}

D

是我太菜.jpg

这个题切不掉真的是傻了啊.jpg

花了好久才搞出来,期间还有来自$app$的提示.jpg

我们发现答案具有积性,也就是说,直接对每个质数维护一遍即可.jpg

$f[i][j]$表示,当前这个质数还有$j$个,已经做了$i$次操作的概率...

转移的话...就直接每个操作的概率做一下就好了.jpg

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 60
#define K 10005
#define mod 1000000007 
#define ll long long
ll P=1,Q;
ll a[N],b[N];
int cnt;
ll qpow(ll x,ll n){ll ret=1;for(;n;n>>=1,x=x*x%mod)if(n&1)ret=ret*x%mod;return ret;}
struct WC
{
    ll x,y;
    WC operator + (const WC &a) const
    {
        WC re;
        re.x=a.x*y%mod+a.y*x%mod;
        re.y=a.y*y%mod;
        return re;
    }
    WC operator * (const WC &a) const 
    {
        WC re;
        re.x=a.x*x%mod;
        re.y=a.y*y%mod;
        return re;
    }
    WC operator * (ll b) const
    {
        WC re;
        (re.x=x*b)%=mod; re.y=y;
        return re;
    }
}f[K][N];
int main()
{
    ll n,k; cin >> n >> k ;
    for(ll i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            a[++cnt]=i; while(n%i==0) b[cnt]++,n/=i;
        }
    }
    if(n!=1) a[++cnt]=n,b[cnt]=1;
    WC Ans=(WC){1,1},Now=(WC){0,1};
    for(int i=1;i<=cnt;i++)
    {
        for(int j=0;j<=k;j++)
        {
            for(int l=0;l<=b[i];l++)
            {
                f[j][l]=(WC){0,1};
            }
        }
        f[0][b[i]]=(WC){1,1};
        for(int j=1;j<=k;j++)
        {
            for(int l=0;l<=b[i];l++)
            {
                for(int r=l;r<=b[i];r++)
                {
                    f[j][l]=f[j][l]+(f[j-1][r]*(WC){1ll,r+1});
                }
            }
        }
        Now=(WC){0,1};
        for(int j=0;j<=b[i];j++) Now=Now+(f[k][j]*qpow(a[i],j));
        Ans=Ans*Now;
    }
    cout << Ans.x*qpow(Ans.y,mod-2)%mod << endl ;
    return 0;
}

E

不会

F

直接bitset就可以了...

然后维护俩bitset,分别表示:这个集合所有数的约数的bitset,莫比乌斯函数的是否为$0$的bitset

然后就可以了...

原理嘛...直接用容斥原理推就好了.jpg

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 100005
bitset<7005>ans[N],G[7005],F[7005];
int miu[7005],pri[7005],vis[7005],cnt;
void Miu()
{
    miu[1]=1;
    for(int i=2;i<=7000;i++)
    {
        if(!vis[i])pri[++cnt]=i,miu[i]=-1;
        for(int j=1;j<=cnt&&i*pri[j]<=7000;++j)
        {
            vis[i*pri[j]]=1;if(i%pri[j]==0)break;
            miu[i*pri[j]]=-miu[i];
        }
    }
} 
int n,Q,op,x,y,z;
int main() {
    Miu();
    for(int i=1;i<=7000;i++)
        for(int j=i;j<=7000;j+=i)
            G[j][i]=1,F[i][j]=(miu[j/i]?1:0);
    scanf("%d%d",&n,&Q);
    while(Q--)
    {
        scanf("%d%d%d",&op,&x,&y);
        if(op==1)ans[x]=G[y];
        else if(op==2)scanf("%d",&z),ans[x]=ans[y]^ans[z];
        else if(op==3)scanf("%d",&z),ans[x]=ans[y]&ans[z];
        else printf("%d",((ans[x]&F[y]).count()&1));
    }puts("");
}

G

比赛的时候不会第二类斯特林数真是遗憾.jpg

直接斯特林数反演一下:$x^K=sumlimits_{k=0}{K,k } imes (x,k) imes k!$

然后就是$ans=sumlimits_{xsubset {1,2,3...n},x eq 0}sumlimits_{k=0}^{kle f(x)}{K,k} imes (f(x),k) imes k!$

换一个枚举顺序就变成了$ans=sumlimits_{k=0}{K,k} imes k!sumlimits_{xsubset{1,2,3...n}}(f(x),k)$

然后就可以把问题转化为维护一个$f(x)$的组合数,项数最多为$K$项,也就是变成了一个$O(nk)$的树上背包问题

转移:$f[x][i]=g[i]+sumlimits_{j=0}^{i-1}g[j] imes f[to][i-j]$,意义:$(f(x),i)=sumlimits_{j=0}^{i-1}(f(x)-i+j,j)$

然后可以在每个位置更新答案,也就是$ans[i]+=sumlimits_{j=1}^{i-1}g[j] imes f[to][i-j]$,同样,直接其他就不选了的方案贡献出来

然后同时也需要处理每条边的贡献:$f[to][i]+=f[to][i-1] imes (i eq1?1:1-2^{-siz[to]})$表达的含义很简单,就是这个子树里选择了一个节点的概率,也就是这条边对答案的贡献...

并且也需要考虑贡献答案,$ans[i]+=f[to][i-1] imes (i==1?1:1-2^{-siz[to]}) imes (1-2^{siz[to1]-n})$,同样,代表的意义是处理这个子树之外,其他的节点至少选了一个的概率...

因为对于$(f(x),1)=f(x)$,所以直接将所有的边的概率相加即可,也就是上述式子。

然后就完事了.jpg

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 100005
#define M 205
#define ll long long
#define mod 1000000007
int q_pow(int x,int n){int ret=1;for(;n;n>>=1,x=(ll)x*x%mod)if(n&1)ret=(ll)ret*x%mod;return ret;}
#define inv(x) q_pow(x,mod-2)
struct node{int to,next;}e[N<<1];
int head[N],siz[N],cnt,f[N][M],s[M][M],fac[M],mi[N],ans[M],n,K;
inline void add(int x,int y){e[cnt]=(node){y,head[x]};head[x]=cnt++;}
void dfs(int x,int from)
{
    siz[x]=1;f[x][0]=1;
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int to1=e[i].to;
        if(to1!=from)
        {
            dfs(to1,x);
            for(int j=min(siz[to1],K)-1;~j;j--)
            {
                int val=f[to1][j];
                if(!j)val=(ll)val*(1-mi[siz[to1]])%mod;
                ans[j+1]=(ans[j+1]+(ll)val*(1-mi[n-siz[to1]]))%mod;
                f[to1][j+1]=(f[to1][j+1]+val)%mod;
            }
            for(int j=min(siz[x]-1,K);~j;j--)
                for(int k=1;k<=siz[to1]&&j+k<=K;k++)
                {
                    int val=(ll)f[to1][k]*f[x][j]%mod;
                    if(j)ans[j+k]=(ans[j+k]+val)%mod;
                    f[x][j+k]=(f[x][j+k]+val)%mod;
                }
            siz[x]+=siz[to1];
        }
    }
}
#define inv2 500000004
int main()
{
    scanf("%d%d",&n,&K);memset(head,-1,sizeof(head));s[0][0]=1;
    mi[0]=1;for(int i=1;i<=n;i++)mi[i]=(ll)mi[i-1]*inv2%mod;
    for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);dfs(1,0);
    for(int i=1;i<=K;i++)for(int j=1;j<=i;j++)s[i][j]=((ll)s[i-1][j]*j+s[i-1][j-1])%mod;
    fac[0]=1;for(int i=1;i<=K;i++)fac[i]=(ll)fac[i-1]*i%mod;int ret=0;
    for(int i=1;i<=K;i++)ret=(ret+(ll)s[K][i]*ans[i]%mod*fac[i])%mod;ret=(ll)ret*q_pow(2,n)%mod;
    printf("%d
",(ret+mod)%mod);
}

//   

H

感觉能做,口胡一发

做一个以$d$为底的倍增,维护这样的信息:$f[i][j]$表示,在原数组$d^i+n$这么长,在原数组$+j$之后的$bitset$大小关系情况。

然后每次倍增求出$f[i+1][j]+=sumlimits_{k=0}^df[i][(j+gen[d])%m]$

这里的转移没想清楚...但是大概是这个样子,然后对$r$和$l-1$分别做一下就好了.jpg

没有代码.jpg

以上是关于Codeforces Hello 2019的主要内容,如果未能解决你的问题,请参考以下文章

sublime text 3 添加代码片段

Hello 2019 F (莫比乌斯反演 + bitset)

c_cpp Codeforces片段

sublime text3 添加新片段

Hello 2019 D 素因子贡献法计算期望 + 概率dp + 滚动数组

如何从URL获取片段标识符(hash#之后的值)?