Examples

2022-7-10 #10 CF1644F & CF643F & CF1588F

第十篇了呢!

感冒了,今天就做一些简单的题目吧。😷🤒

028 CF1644F Basis

之前 edu 没做出来的题。

观察可知,我们选出来的基一定是去掉最后一段之后所有段的 \(\gcd=1\) 的串,我们直接对其计数:

枚举最大的 \(\gcd\) 进行容斥,容易发现容斥系数为 \(\mu(k)\):(注意为避免对空集求 \(\gcd\),我们需要忽略只有一段的情况)

\[\sum_{k=1}^{n}\mu(k)(\sum_{i=1}^k{\lceil\frac{n}{k}\rceil\brace i}-1) \]

而求第二类斯特林数前缀和单项可以线性,具体地,我们列出卷积的式子:

\[{n\brace k}=\frac{1}{k!}\sum_{i=0}^k{k\choose i}(-1)^{k-i}i^n=\sum_{i=0}^k\frac{(-1)^{k-i}}{(k-i)!}\cdot\frac{i^n}{i!} \]

枚举第一维对第二维做前缀和即可,注意 \(i^n\) 需要线性筛。

复杂度 \(O(n\log n)\)

#include<stdio.h>
const int maxn=200005,mod=998244353;
int n,m,ans,ps;
int c[maxn],p[maxn],miu[maxn],mul[maxn],rec[maxn],A[maxn],B[maxn],nfac[maxn],inv[maxn];
void sieve(int n){
	c[1]=miu[1]=inv[1]=nfac[0]=nfac[1]=1;
	for(int i=2;i<=n;i++){
		if(c[i]==0)
			p[++ps]=i,miu[i]=-1;
		rec[i]=ps,inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod,nfac[i]=1ll*nfac[i-1]*inv[i]%mod;
		for(int j=1;j<=ps&&i*p[j]<=n;j++){
			c[i*p[j]]=1;
			if(i%p[j]==0)
				break;
			miu[i*p[j]]=-miu[i];
		}
	}
}
int ksm(int a,int b){
	int res=1;
	while(b){
		if(b&1)
			res=1ll*res*a%mod;
		a=1ll*a*a%mod,b>>=1;
	}
	return res;
}
int calc(int n,int k){
	if(k>n)
		k=n;
	mul[1]=1;
	for(int i=2;i<=k;i++){
		if(c[i]==0)
			mul[i]=ksm(i,n);
		for(int j=1;j<=rec[i]&&i*p[j]<=n;j++){
			mul[i*p[j]]=1ll*mul[i]*mul[p[j]]%mod;
			if(i%p[j]==0)
				break;
		}
	}
	int res=0;
	for(int i=1;i<=k;i++)
		B[i]=(B[i-1]+1ll*mul[i]*nfac[i]%mod)%mod;
	for(int i=0;i<=k;i++)
		res=(res+1ll*A[i]*B[k-i])%mod;
	return res;
}
int main(){
	scanf("%d%d",&n,&m);
	if(n==1||m==1){
		puts("1");
		return 0;
	}
	sieve(n);
	for(int i=0;i<=n;i++)
		A[i]=1ll*((i&1)? mod-1:1)*nfac[i]%mod;
	for(int i=1;i<=n;i++)
		if(miu[i])
			ans=(ans+1ll*(miu[i]+mod)*(calc((n+i-1)/i,m)-1+mod))%mod;
	printf("%d\n",ans);
	return 0;
}

029 CF643F Bears and Juice

比较神奇的题。

我们考虑熊能表示出的状态数是多少:

\[\sum_{i=0}^{\min(p,n-1)}{n\choose i}t^i \]

表示枚举有几只熊睡觉,如果睡了是在哪一天睡的。

可以构造策略使得其恰好能辨认出第 \(1,2,\cdots,\sum_{i=0}^{\min(p,n-1)}{n\choose i}t^i\) 这些酒桶。

假设是第 \(k\) 个桶是酒,我们只需让哪些醉了的熊恰好在它们对应的天喝这桶,其他的不喝即可。

于是我们要对于 \(t\in[1,q]\) 快速计算上式,我们可以 \(O(pq)\) 枚举,对于所有 \(i\leqslant\min(p,n-1)\) 预处理 \(n\choose i\) 即可 \(O(1)\) 计算。

复杂度 \(O(pq)\)

#include<stdio.h>
const int maxn=205;
int n,p,q;
int v[maxn];
unsigned int ans;
unsigned int C[maxn];
int gcd(int a,int b){
	return b==0? a:gcd(b,a%b);
}
int main(){
	scanf("%d%d%d",&n,&p,&q);
	for(int i=0;i<=p&&i<=n-1;i++){
		for(int j=1;j<=i;j++)
			v[j]=n-j+1;
		for(int j=1;j<=i;j++){
			int w=j;
			for(int k=1;k<=i;k++){
				int g=gcd(w,v[k]);
				w/=g,v[k]/=g;
			}
		}
		C[i]=1;
		for(int j=1;j<=i;j++)
			C[i]*=v[j];
	}
	for(int i=1;i<=q;i++){
		unsigned int res=0,mul=1;
		for(int j=0;j<=p&&j<=n-1;j++,mul*=i)
			res+=C[j]*mul;
		ans^=i*res;
	}
	printf("%u\n",ans);
	return 0;
}

030 CF1588F Jumping Through the Array

2e5 8s,考虑操作分块。

一开始以为是基环树。。。

先考虑没有 3 操作的情况,询问的时候枚举这个块内的修改,可以 \(O(1)\) 计算这个环内的区间内结点数量,走完一个操作块就重新计算一下权值。

有 3 操作时,我们不方便直接维护每个环,也不方便重新计算权值,我们考虑能不能暴力处理这件事。

实际上是可以的,我们每次提取出操作影响到的点作为关键点,把没有修改过的点挂在关键点上面,我们维护这样一个 \(O(\sqrt n)\) 的图暴力修改查询就好了。注意我们要对于每个环维护一个二维前缀和状物,发现两维都可以离散化到 \(O(\sqrt n)\),暴力处理即可。

复杂度 \(O((n+q)\sqrt q)\)

头痛,懒得写严格根号了。。。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=200005;
int n,q,ps;
int p[maxn],qo[maxn],qx[maxn],qy[maxn],bel[maxn],pos[maxn];
long long a[maxn],sum[maxn],lazy[maxn];
vector<int>v[maxn];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++)
		scanf("%d",&p[i]),bel[i]=-1;
	scanf("%d",&q);
	for(int L=1,R;L<=q;L=R+1){
		R=min(R+500,q),ps=0;
		for(int i=L;i<=R;i++){
			scanf("%d%d%d",&qo[i],&qx[i],&qy[i]);
			if(qo[i]==2)
				bel[qx[i]]=qx[i];
			if(qo[i]==3)
				bel[qx[i]]=qx[i],bel[qy[i]]=qy[i];
		}
		for(int i=1;i<=n;i++)
			if(bel[i]==-1){
				int j,k,f=1;
				for(j=p[i];j!=i&&bel[j]==-1;j=p[j]);
				for(k=i;(k!=i&&bel[k]==-1)||f;k=p[k])
					bel[k]=j==i? 0:bel[j],f=0;
			}
		for(int i=1;i<=n;i++){
			sum[i]=sum[i-1]+a[i];
			if(bel[i]==i)
				pos[++ps]=i;
			if(bel[i]>0)
				v[bel[i]].push_back(i);
		}
		for(int i=L;i<=R;i++){
			int o=qo[i],x=qx[i],y=qy[i];
			if(o==1){
				long long res=sum[y]-sum[x-1];
				for(int j=1;j<=ps;j++)
					res+=1ll*(upper_bound(v[pos[j]].begin(),v[pos[j]].end(),y)-lower_bound(v[pos[j]].begin(),v[pos[j]].end(),x))*lazy[pos[j]];
				printf("%lld\n",res);
			}
			if(o==2){
				int flg=1;
				for(int i=x;i!=x||flg;i=bel[p[i]])
					lazy[i]+=y,flg=0;
			}
			if(o==3)
				swap(p[x],p[y]);
		}
		for(int i=1;i<=n;i++)
			if(bel[i]>0)
				a[i]+=lazy[bel[i]];
		for(int i=1;i<=n;i++)
			bel[i]=-1,lazy[i]=0,v[i].clear();
	}
	return 0;
}
posted @ 2022-07-10 12:07  xiaoziyao  阅读(80)  评论(2编辑  收藏  举报