【CF套题】Hello 2019 迟(gu)到(diao)的总结

【前言】
最后打了rank85,+152上到Master了。
结果还不错,就是又是靠手速而不是靠题数,感觉有点蒻。

【题目】
原题地址

A.Gennady and a Card Game

模拟。

#include<bits/stdc++.h>
#define mkp make_pair
#define pb push_back
#define fi first
#define se second
using namespace std;

typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
char s[4],t[4];

int read()
{
	int ret=0,f=1;char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return f?ret:-ret;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("A.in","r",stdin);
	freopen("A.out","w",stdout);
#endif
	scanf("%s",s);int flag=0;
	for(int i=1;i<=5;++i)
	{
		scanf("%s",t);
		if(s[0]==t[0] || s[1]==t[1]){flag=1;break;}
	}
	puts(flag?"YES":"NO");
	return 0;
}

B.Petr and a Combination Lock

枚举每一步是顺时针还是逆时针模拟。

#include<bits/stdc++.h>
#define mkp make_pair
#define pb push_back
#define fi first
#define se second
using namespace std;

typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
int n,a[20];

int read()
{
	int ret=0,f=1;char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return f?ret:-ret;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("B.in","r",stdin);
	freopen("B.out","w",stdout);
#endif
	n=read();int S=1<<n;
	for(int i=0;i<n;++i) a[i]=read();
	for(int i=0;i<S;++i)
	{
		int sum=0;
		for(int j=0;j<n;++j) if(i&(1<<j)) sum+=a[j]; else sum-=a[j];
		if(abs(sum)%360==0) {puts("YES");return 0;}
	}
	puts("NO");
	return 0;
}

C.Yuhao and a Parenthesis

计算出每个括号序列的权值后统计。

#include<bits/stdc++.h>
#define mkp make_pair
#define pb push_back
#define fi first
#define se second
using namespace std;

typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f,N=5e5+10;
int ans,n;
char s[N];
map<int,int>::iterator it;
map<int,int>mp;

int read()
{
	int ret=0,f=1;char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return f?ret:-ret;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("C.in","r",stdin);
	freopen("C.out","w",stdout);
#endif
	n=read();
	for(int i=1;i<=n;++i)
	{
		scanf("%s",s);int m=strlen(s);
		int sum=0,mi=0,mx=0;
		for(int j=0;j<m;++j) 
			if(s[j]==')') --sum,mi=min(mi,sum);
			else ++sum;
		sum=0;
		for(int j=m-1;~j;--j) 
			if(s[j]=='(') ++sum,mx=max(mx,sum);
			else --sum;
		if(mx!=0 && mi!=0) continue;
		mp[mx+mi]++;
		//cerr<<mx+mi<<endl;
	}
	for(it=mp.begin();it!=mp.end();++it)
	{
		while(((*it).se) && mp[-((*it).fi)]>0)
			if((*it).fi==0 && (*it).se<=1) break;
			else ++ans,--mp[-((*it).fi)],--mp[(*it).fi];
	}
	printf("%d\n",ans);
	return 0;
}

D.Makoto and a Blackboard

显然每种质因子是独立的,分解质因数后,设 f i , j f_{i,j} fi,j表示第 i i i种质因子剩余 j j j个的概率,进行 k k k DP \text{DP} DP即可。

#include<bits/stdc++.h>
#define mkp make_pair
#define pb push_back
#define fi first
#define se second
using namespace std;

typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f,N=1e4+10,mod=1e9+7;
ll n,K,cnt;
ll a[N],b[N],inv[N],f[N][70];

int read()
{
	int ret=0,f=1;char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return f?ret:-ret;
}

ll qpow(ll x,ll y)
{
	ll res=1;
	for(;y;y>>=1,x=x*x%mod)if(y&1)res=res*x%mod;
	return res;
}
void up(ll &x,ll y){x+=y;if(x>=mod)x-=mod;}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("D.in","r",stdin);
	freopen("D.out","w",stdout);
#endif
	scanf("%lld%lld",&n,&K);inv[0]=inv[1]=1;
	for(int i=2;i<N;++i) inv[i]=qpow(i,mod-2);
	for(int i=2;n && i<=sqrt(n);++i)
	{
		if(n%i) continue;
		++cnt;b[cnt]=i;
		while(!(n%i)) a[cnt]++,n/=i;
	}	
	if(n)
	{
		++cnt;b[cnt]=n;a[cnt]=1;
	}
	for(int i=1;i<=cnt;++i) f[i][a[i]]=1;
	while(K--)
	{
		for(int j=1;j<=cnt;++j)
		{
			ll sum=0;
			for(int k=a[j];~k;--k) 
			{
				up(sum,inv[k+1]*f[j][k]%mod);
				f[j][k]=sum;
			}
		}
	}
	//printf("%lld %lld\n",f[1][0],f[1][1]);
	ll ans=1;
	for(int i=1;i<=cnt;++i)
	{
		ll res=0,c=1;
		for(int j=0;j<=a[i];++j,c=c*b[i]%mod) up(res,f[i][j]*c%mod);
		ans=ans*res%mod;
	}
	printf("%lld\n",ans);

	return 0;
}

E.Egor and an RPG game

这个题一直以为是划分成最少的,一直觉得不可做,补题认真一看。。。
结论是对于一个数 k k k,若 n &lt; k ( k + 1 ) 2 n&lt;\frac {k(k+1)} 2 n<2k(k+1),我们总是能用最多 k − 1 k-1 k1个子序列完成任务。
为什么?因为若 LIS ≥ k \text{LIS}\geq k LISk,我们可以得到一个更小的子任务 ( k − 1 ) k 2 \frac {(k-1)k} 2 2(k1)k,否则,根据 Dilworth \text{Dilworth} Dilworth 定理,该序列可以被拆分为 x &lt; k x&lt;k x<k 个下降子序列,并且我们可以在求解最长上升子序列时顺带解决将其拆分为 x x x个下降子序列的问题。

#include<bits/stdc++.h>
#define mkp make_pair
#define pb push_back
#define fi first
#define se second
using namespace std;

typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f,N=1e5+10;
int n,K,top;
int a[N],sta[N],vis[N],las[N],pre[N],f[N];
vector<int>vec[N];

int read()
{
	int ret=0,f=1;char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return f?ret:-ret;
}

int getlen()
{
	for(int i=1;i<=top;++i) vis[i]=0;
	sta[top=1]=INF;
	for(int i=1;i<=n;++i)
	{
		int p=upper_bound(sta+1,sta+top+1,a[i])-sta;
		sta[p]=a[i];las[i]=vis[p];vis[p]=i;pre[i]=vis[p-1];
		if(p==top) sta[++top]=INF;
	}
	return top-1;
}
void LDS(int len)
{
	vec[++K].clear();
	for(int i=vis[len];i;i=pre[i]) vec[K].pb(a[i]);
}
void LIS(int len)
{
	for(int i=1;i<=len;++i)
	{
		vec[++K].clear();
		for(int j=vis[i];j;j=las[j]) vec[K].pb(a[j]);
	}
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("E.in","r",stdin);
	freopen("E.out","w",stdout);
#endif
	int T=read();
	while(T--)
	{
		n=read();K=0;
		for(int i=1;i<=n;++i) a[i]=read();
		while(n)
		{
			int len=getlen();
			if(n<=(ll)len*(len+1)/2) LDS(len);
			else {LIS(len);break;}
			int tn=n,j=len;n=0;
			f[j+1]=0;
			for(int i=vis[len];i;i=pre[i])f[j--]=a[i];
			++j;
			for(int i=1;i<=tn;++i) 
				if(a[i]^f[j]) a[++n]=a[i]; else ++j;
		}
		printf("%d\n",K);
		for(int i=1;i<=K;++i)
		{
			printf("%d ",(int)vec[i].size());reverse(vec[i].begin(),vec[i].end());
			for(int j=0;j<(int)vec[i].size();++j) printf("%d ",vec[i][j]);
			puts("");
		}
	}
	return 0;
}

F.Alex and a TV Show

我们对每个集合维护一个 bitset \text{bitset} bitset表示 i i i的倍数出现次数的奇偶性。
因为是在模 2 2 2意义下统计结果,所以操作 2 2 2我们可以直接异或。
操作 3 3 3是一个卷积,但实际上可以构造正变换和逆变换使卷积变为点乘,即 a n d and and。(手玩也行)
操作 4 4 4就是一个反演了,即 v i s x = ∑ x ∣ d μ ( d n ) f d vis_x=\sum_{x|d}\mu(\frac d n) f_d visx=xdμ(nd)fd
预处理7000个 bitset \text{bitset} bitset表示第i个数的反演有哪些数作贡献,然后就是一个 a n d and and了。

#include<bits/stdc++.h>
#define mkp make_pair
#define pb push_back
#define fi first
#define se second
using namespace std;

typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int V=7010,N=1e5+10;
int n,m,pnum;
int bo[V],mu[V],pri[V];
bitset<V> v[V],q[N],b[N];

int read()
{
	int ret=0,f=1;char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return f?ret:-ret;
}

void init()
{
	mu[1]=1;
	for(int i=2;i<V;i++)
	{
		if(!bo[i]) pri[pnum++]=i,mu[i]=-1;
		for(int j=0;j<pnum && i*pri[j]<V;j++)
		{
			bo[i*pri[j]]=1;
			if(!(i%pri[j])){mu[i*pri[j]]=0;break;} 
			mu[i*pri[j]]=-mu[i];
		}
	}
	for(int i=1;i<V;i++)
	{
		for(int j=1;j<=i;j++) if(!(i%j)) v[i][j]=1;
		for(int j=i;j<V;j+=i) q[i][j]=!!mu[j/i];
	}
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("F.in","r",stdin);
	freopen("F.out","w",stdout);
#endif
	init();
	n=read();m=read();
	while(m--)
	{
		int op=read(),x=read(),y=read(),z;
		if(op==1) b[x]=v[y];
		else if(op==2) z=read(),b[x]=b[y]^b[z];
		else if(op==3) z=read(),b[x]=b[y]&b[z];
		else putchar(((b[x]&q[y]).count()&1)^48);
	}
	return 0;
}

G.Vladislav and a Great Legend

假设 k = 1 k=1 k=1,我们可以简单 DP \text{DP} DP,在 LCA \text{LCA} LCA处统计贡献。
现在 k &gt; 1 k&gt;1 k>1,我们用二项式展开做到 O ( n k 2 ) O(nk^2) O(nk2),然而并不优秀。
那么考虑 x k = ∑ i = 0 k ( x i ) S ( k , i ) i ! x^k=\sum_{i=0}^k{x\choose i}S(k,i)i! xk=i=0k(ix)S(k,i)i!
其中 S ( i , j ) S(i,j) S(i,j)表示第二类斯特林数,拆开组合数后就是
x k = ∑ i = 0 k x ! ( x − i ) ! S ( k , i ) x^k=\sum_{i=0}^k\frac {x!} {(x-i)!}S(k,i) xk=i=0k(xi)!x!S(k,i)
于是我们只需要维护所有的下降幂就可以还原答案。
注意我们枚举的上界只用枚举到 min ⁡ ( 当 前 非 0 下 降 幂 长 度 , k ) \min(当前非0下降幂长度,k) min(0,k),这样可以保证复杂度是 O ( n k ) O(nk) O(nk)的,证明就是树上依赖背包。

#include<bits/stdc++.h>
#define mkp make_pairs
#define pb push_back
#define fi first
#define se second
using namespace std;

typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=1e9+7,inv2=(mod+1)>>1;
const int N=1e5+10,M=205;
int n,K,tot,ans;
int sz[N],head[N],S[M][M];
int res[N],fc[N],f[N][M];

int read()
{
	int ret=0,f=1;char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return f?ret:-ret;
}

struct Tway{int v,nex;}e[N<<1];
void add(int u,int v)
{
	e[++tot]=(Tway){v,head[u]};head[u]=tot;
	e[++tot]=(Tway){u,head[v]};head[v]=tot;
}

void dfs(int x,int fa)
{
	f[x][0]=sz[x]=1;
	for(int i=head[x];i;i=e[i].nex)
	{
		int v=e[i].v;
		if(v==fa) continue; dfs(v,x);
		for(int j=min(sz[v]-1,K-1);~j;--j)
		{
			int val=f[v][j];
			if(!j) val=(ll)val*(1-fc[sz[v]]+mod)%mod;
			(res[j+1]+=((ll)val*(1-fc[n-sz[v]]+mod)%mod+mod)%mod)%=mod;
			(f[v][j+1]+=val)%=mod;
		}
		for(int j=min(sz[x]-1,K);~j;--j) for(int k=1;k<=sz[v] && j+k<=K;++k)
		{
			int val=(ll)f[x][j]*f[v][k]%mod;
			if(j) (res[j+k]+=val)%=mod;
			(f[x][j+k]+=val)%=mod;
		}
		sz[x]+=sz[v];
	}
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("G.in","r",stdin);
	freopen("G.out","w",stdout);
#endif
	n=read();K=read();
	S[0][0]=1;
	for(int i=1;i<=K;++i) for(int j=1;j<=i;++j)
		S[i][j]=(ll)(S[i-1][j-1]+(ll)S[i-1][j]*j%mod)%mod;
	fc[0]=1;for(int i=1;i<=n;++i)fc[i]=(ll)fc[i-1]*inv2%mod;
	for(int i=1;i<n;++i) add(read(),read());
	dfs(1,0);
	for(int i=0,jc=1;i<=K;++i,jc=(ll)jc*i%mod) (ans+=(ll)S[K][i]*jc%mod*res[i]%mod)%=mod;
	for(int i=1;i<=n;++i) ans=ans*2%mod;
	printf("%d\n",ans);
	return 0;
}

H.Mateusz and an Infinite Sequence

题目十分复杂,而且不会做qwq。

考虑如何描述一个区间内的元素。
我们需要记录其长度 l e n len len,包含可行子串的个数 a n s ans ans,其长度为 i i i的后缀能否作为可行子串长度为 i i i的前缀(没有出现的元素当作 0 0 0),以及长度为 n − i n-i ni的前缀能否作为可行子串长度为 n − i n-i ni的后缀(同上)。

上面的信息可以用 bitset \text{bitset} bitset进行合并,做到 O ( n w ) O(\frac n w) O(wn)
接下来我们还需要做一个数位 DP \text{DP} DP。令 f i , j f_{i,j} fi,j表示序列的前 d i d^i di位加上 j j j后的信息即可。
复杂度 O ( n d m log ⁡ m ω ) O(\frac {ndm\log m} {\omega }) O(ωndmlogm)

#include<bits/stdc++.h>
#define mkp make_pair
#define pb push_back
#define fi first
#define se second
using namespace std;

typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f,N=3e4+10,M=65;
int n,m,d,tot,gen[M],b[N];
ll ql,qr,len[M];
bitset<N>all,tmp;

ll read()
{
	ll ret=0,f=1;char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return f?ret:-ret;
}

struct data
{
	ll len,ans;
	bitset<N>pre,suf;
}f[M][M];
data operator +(const data&a,const data&b)
{
	if(!a.len) return b;
	data res;
	res.len=a.len+b.len;res.ans=a.ans+b.ans;
	res.pre=a.pre;res.suf=b.suf;
	if(a.len<=n-2) res.pre&=(b.pre>>a.len)|(all<<(n-a.len-1));
	if(b.len<=n-2) res.suf&=(a.suf<<b.len)|(all>>(n-b.len-1));
	if(a.len+b.len>=n)
	{
		tmp=a.suf&b.pre;
		if(a.len<=n-2) tmp&=all>>(n-a.len-1);
		if(b.len<=n-2) tmp&=all<<(n-b.len-1);
		res.ans+=tmp.count();
	}
	return res;
}
ll solve(ll x)
{
	data res;int delta=0;
	res.ans=res.len=0;
	res.pre.reset();res.suf.reset();
	for(int i=tot;~i;--i) for(int j=1;j<=d;++j)
	{
		if(x>=len[i]) x-=len[i],res=res+f[i][(delta+gen[j])%m];
		else {delta=(delta+gen[j])%m;break;}
	}
	return res.ans;
}
void init()
{
	d=read();m=read();
	for(int i=1;i<=d;++i) gen[i]=read();
	n=read();
	for(int i=1;i<=n;++i) b[i]=read();
	for(int i=1;i<n;++i) all.set(i);	
	len[0]=1;
	for(int i=0;i<m;++i)
	{
		f[0][i].len=1;
		if(n==1) f[0][i].ans=i<=b[1];
		else
		{
			for(int j=1;j<n;++j)
			{
				f[0][i].pre[j]=i<=b[j+1];
				f[0][i].suf[j]=i<=b[j];
			}
		}
	}
}
void work()
{
	ql=read();qr=read();
	while(len[tot]<=qr/d)
	{
		++tot;len[tot]=len[tot-1]*d;
		for(int i=0;i<m;++i) for(int j=1;j<=d;++j)
			f[tot][i]=f[tot][i]+f[tot-1][(i+gen[j])%m];
	}
	printf("%lld\n",solve(qr)-solve(ql+n-2));
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("H.in","r",stdin);
	freopen("H.out","w",stdout);
#endif
	init();work();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值