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\n",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=\sum\limits_{k=0}{K,k }\times (x,k)\times k!$

然后就是$ans=\sum\limits_{x\subset {1,2,3...n},x\neq 0}\sum\limits_{k=0}^{k\le f(x)}{K,k}\times (f(x),k)\times k!$

换一个枚举顺序就变成了$ans=\sum\limits_{k=0}{K,k}\times k!\sum\limits_{x\subset{1,2,3...n}}(f(x),k)$

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

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

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

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

并且也需要考虑贡献答案,$ans[i]+=f[to][i-1]\times (i==1?1:1-2^{-siz[to]})\times (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\n",(ret+mod)%mod);
}

//   

H

感觉能做,口胡一发

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

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

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

没有代码.jpg

posted @ 2019-02-05 23:56  Winniechen  阅读(222)  评论(0编辑  收藏  举报