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的主要内容,如果未能解决你的问题,请参考以下文章
Hello 2019 F (莫比乌斯反演 + bitset)