不知道叫什么标题了

Codeforces 1129D Isolation

一个长度为(n)的序列,上面每个位置有一种颜色,求把这个序列分割成若干段,使得每一段的只出现一次的颜色个数不超过(k)个,求方案数。

(n le 10^5)

(dp[i])表示(1...i)的合法划分方案数

显然 (dp[i]=sum_{j=1}^{i-1}dp[j][cnt(j,i) le k])

对于(cnt(j,i))的计算:

定义数组(b),如果([i,j])(a_j)的颜色第一次出现,那么(b_j=1),如果是第二次出现,那么(b_j=-1),否则(b_j=0)

那么(cnt(j,i)=sum_{k=j}^ib_k)

考虑(i)转移到(i+1)的过程,我们发现只有一个(b_p)(1)变成(-1) ,有一个(b_q)(-1)变成(0)

考虑(cnt(j,i+1))相对于(cnt(j,i))的变化本质就是([p+1,i])区间​(+1),区间([q+1,p]-1),其余不变

那么我们需要支持两种操作:

  1. 区间加减(1)
  2. 求所有满足(cnt(j,i)le k) 的所有(dp[j])之和

使用分块维护

时间复杂度(O(nsqrt n))

ARC060E Representation

题意:

把一个长度为 (len) 的串切成 (k) 段,使得其每一段都不是循环的(由一个子串出现 (ge 2) 次构成),求最小的 (k) ,以及在最小的 (k) 之下的方案数,对 (10^9+7) 取模。

(len le 5 imes 10^5)

题解:

(并不)容易发现, (k) 只有3种取值,(1,2,len) 考虑原串如果不是循环的,那么答案为 (1) ,如果是循环的且循环节长度 (>1) ,那么随便找一个循环节中间断开即可,如果全是同一种字母,显然没有办法,只能切成 (len)

求出 (k) 之后就可以很容易的统计方案数了, (k=1,len) 答案显然,对于 (k=2) 求出正串和反串的 (next) 数组,枚举断点即可

#include <cstdio>
#include <cstring>
const int N=500005;
int nxt1[N],nxt2[N];
char s1[N],s2[N];
inline void getnxt(char *s,int *t){
	int n=strlen(s+1);
	for (int i=2,k=0;i<=n;i++){
		while (k&&s[k+1]!=s[i]) k=t[k];
		if (s[k+1]==s[i]) k++;t[i]=k;
	}
}
int main (){
	scanf ("%s",s1+1);
	int n=strlen(s1+1),cnt=0,f=1;
	for (int i=2;i<=n;i++) if (s1[i]!=s1[i-1]) f=0;
	if (f){
		printf ("%d
1",n,1);
		return 0;
	}
	getnxt(s1,nxt1);
	for (int i=1;i<=n;i++) s2[i]=s1[n-i+1];
	getnxt(s2,nxt2);
	if ((!nxt1[n])||(n%(n-nxt1[n])!=0)) {
		puts("1
1");
		return 0;
	}
	for (int i=1;i<n;i++)
		if (((!nxt1[i])||(i%(i-nxt1[i])!=0))&&((!nxt2[n-i])||((n-i)%(n-i-nxt2[n-i])!=0))) cnt++;
	printf ("2
%d",cnt);
	return 0;
}

ARC064D Rotated Palindromes

题意:

给定数字 (n,k) ,求有多少长度为 (n) 的序列 (a),同时满足以下两个条件:

  1. 对于任意 (1le ile n)(a_iin[1,k])
  2. 序列 (a) 可以通过循环位移变成回文串

答案对 (10^9+7) 取模 (n,kle 10^9)

题解:

如果没有循环节的限制,答案就是 (k^{lceilfrac{n}{2} ceil}) 次方

容斥,设 (dp[x]) 为最小循环节长度为 (v[x]) 时有多少个回文序列,转移时要减去它的约数的答案

考虑对答案的贡献:如果 (v[x]) 是奇数,贡献是 (v[x] imes dp[x]) ,否则贡献是 (frac{v[x]}{2} imes dp[x]) ,因为这一段必定是回文的,重复个数为一半

#include <bits/stdc++.h>
using namespace std;
const int N=2005,Mod=1e9+7;
int v[N],f[N];
inline int qpow(int a,int b){
	int ans=1;
	while (b){
		if (b&1) ans=1ll*ans*a%Mod;
		a=1ll*a*a%Mod,b>>=1;
	}
	return ans;
}
int main(){
    int n,k,tot=0,ans=0;
    scanf ("%d%d",&n,&k);
    for (int i=1; i*i<=n; i++)
        if (n%i==0) {
			v[++tot]=i;
			if (i*i!=n) v[++tot]=n/i;
		}
    sort(v+1,v+tot+1);
    for (int i=1; i<=tot; i++){
        f[i]=qpow(k,(v[i]+1)/2);
        for (int j=1; j<i; j++) if (v[i]%v[j]==0) f[i]=(f[i]-f[j]+Mod)%Mod;
        if (v[i]&1) ans=(1ll*ans+1ll*v[i]*f[i]%Mod)%Mod;
        else ans=(1ll*ans+1ll*(v[i]/2)*f[i]%Mod)%Mod;
    }
    printf ("%d",ans);
    return 0;
}

ARC063C Integers on a Tree

题意和题解就直接放 zld 的集训队作业了......orz zld!!!


#include <bits/stdc++.h>
using namespace std;
const int N=100005;
int Head[N],Next[N<<1],Adj[N<<1],tot=0;
inline void addedge(int u,int v){
	Next[++tot]=Head[u],Head[u]=tot,Adj[tot]=v;
	Next[++tot]=Head[v],Head[v]=tot,Adj[tot]=u;
}
int L[N],R[N];
bool flag,p[N];
inline void check(int x,int f,bool t){
	p[x]=t;
	if (L[x]==R[x]) {
		if ((L[x]&1)!=t) {
			flag=false;
			return;
		}
	}
	for (int e=Head[x];e;e=Next[e])
		if (Adj[e]!=f) check(Adj[e],x,t^1);
}
inline int Min(int a,int b){
	return a<b?a:b;
}
inline int Max(int a,int b){
	return a>b?a:b;
}
inline void dfs(int x,int f){
	for (int e=Head[x];e;e=Next[e])
		if (Adj[e]!=f){
			dfs(Adj[e],x);
			L[x]=Max(L[Adj[e]]-1,L[x]);
			R[x]=Min(R[Adj[e]]+1,R[x]);
		}
}
inline void dfs2(int x,int f){
	if (f){
		L[x]=Max(L[f]-1,L[x]);
		R[x]=Min(R[f]+1,R[x]);
		if (L[x]>R[x]){
			puts("No");
			exit(0);
		}
	}
	for (int e=Head[x];e;e=Next[e])
		if (Adj[e]!=f)
			dfs2(Adj[e],x);
}
int a[N];
inline void getans(int x,int f,int lst){
	if (L[x]<=lst-1) a[x]=--lst;
	else a[x]=++lst;
	for (int e=Head[x];e;e=Next[e])
		if (Adj[e]!=f) getans(Adj[e],x,lst);
}
int main (){
	memset (L,-0x3f,sizeof(L));
	memset (R,0x3f,sizeof(R));
	int n;scanf ("%d",&n);
	for (int i=1,u,v;i<n;i++){
		scanf ("%d%d",&u,&v);
		addedge(u,v);
	}
	int m;scanf ("%d",&m);
	int rt=1;
	for (int i=1,x,v;i<=m;i++){
		scanf ("%d%d",&x,&v);
		rt=x;
		L[x]=R[x]=v;
	}
	flag=true,check(rt,0,L[rt]&1);
	if (!flag) {
		puts("No");
		return 0;
	}
	dfs(rt,0);
	dfs2(rt,0);
	puts("Yes");
	getans(rt,0,L[rt]-1);
	for (int i=1;i<=n;i++) printf ("%d
",a[i]);
	return 0;
}

ARC061D Card Game for Three

题意:

A,B,C三个人玩游戏,最初每个人分别有 (n,m,k) 张牌,每张牌上有字母 (a,b,c) 之一,但是不知道每张牌具体是什么。由A先操作,每轮操作者翻出自己牌堆顶的一张牌并弃置,下一轮的操作者是该牌上字母对应的人。不能操作的人赢。问有多少种初始牌的情况能使A胜利。答案模 (10^9+7)

(n,m,kle 3 imes 10^5)

题解:

[Ans=sum_{i=0}^{i<=m+k} inom{n+i-1}{n-1}3^{m+k-1}sum_{0le xle m,0le yle k}[x+y=i]inom{i}{x} ]

原理:枚举抽到了多少张非 (A) 的卡牌,然后 (A) 可以放置的位置是 (inom{n+i-1}{n-1})

剩余没有选择的 (m+k-1) 张卡牌显然随便取

之后我们发现没有计数的是 (B,C) 的相对位置

我们枚举一个 (B) 的个数,计算 (B) 的位置安排方法,然后剩下的地方放 (C)

最后加起来就是 (B,C) 的相对位置

优化:

直接计算显然是 (O(n^2)) 级别的 ((n,m,k) 同阶 ())

我们观察最后那个 (sum) 如何拆开

  1. (i< k) 的时候即为 (sum_{x=0}^{x<k}inom{i}{x})
  2. (i<m) 的时候即为 (sum_{x=0}^{x<m}inom{i}{x})
  3. (mle ile m+k) 的时候即为 (sum_{x=m+1}^{x<k}inom{i}{x})

这三种情况显然可以递推了,类似杨辉三角那样

#include <cstdio>
const int N=1000005,Mod=1e9+7;
inline int qpow(int a,int b){
	int ans=1;
	while (b){
		if (b&1) ans=1ll*ans*a%Mod;
		a=1ll*a*a%Mod,b>>=1; 
	}
	return ans;
}
int fac[N],inv[N],pow3[N];
inline void init_binom(){
	fac[0]=1;for (int i=1;i<=1000000;i++) fac[i]=1ll*fac[i-1]*i%Mod;
	inv[1000000]=qpow(fac[1000000],Mod-2);
	for (int i=999999;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%Mod;
	pow3[0]=1;for (int i=1;i<=1000000;i++) pow3[i]=1ll*pow3[i-1]*3ll%Mod;
}
inline int C(int n,int m){
	return 1ll*fac[n]*inv[m]%Mod*inv[n-m]%Mod;
}
int n,m,k;
int main (){
	init_binom();
	scanf ("%d%d%d",&n,&m,&k);
	int ans=0,x=1;
	if (m<k) m^=k^=m^=k;
	for (int i=0;i<=m+k;i++) {
        ans=(ans+1ll*C(n-1+i,n-1)*pow3[m+k-i]%Mod*x%Mod)%Mod;
        if (i<k) x=x*2ll%Mod;
        else if(i>=m) x=((x*2ll%Mod-C(i,k)-C(i,i-m))%Mod+Mod)%Mod;
        else x=(x*2ll%Mod-C(i,k)+Mod)%Mod;
    }
	printf ("%d",(ans+Mod)%Mod);
	return 0;
}

ARC068 Solitaire

题意:把 (1...n) 顺次加入双端队列(可以从前后加),再删除 (k) 个(可以从前后删),问删到的第 (k) 个是 (1) 的删除序列的个数,答案对 (10^9+7) 取模

(kle nle 2 imes 10^3)

我们考虑合法的删除序列的性质

  1. (k) 个数字是 (1) (废话)
  2. (k-1) 个数字可以拆分成一个或两个(非连续)下降序列
    考虑原因?如果双端队列为 (a_1,a_2,a_3...,1,a_{n-2},a_{n-1},a_n) 那么显然有 (a_1>a_2>a_3) 以及 (a_{n-2}<a_{n-1}<a_n),把从前面删的提取成一个序列,从后面删的提取成一个序列
  3. 由性质 (2) 拆出来的序列中,其中一个的最小值大于没删除的所有数字的最大值

接下来考虑构造:

先考虑剩下来的 (n-k) 个数字,可以随便 pop ,所以

已经弹出的序列,考虑dp,设 (f[i][j]) 表示前 (k-1) 个数字中,已经确定了 (i) 个数字,它们的最小值为 (j)

设删除后两个序列为 (AB)(A) 一直删到 (1)(B) 满足性质 3

那么可以选择下一个数字放在 (A) 后或者 (B)

如果放在 (A) 后,那么新加入的数字一定小于 (j) ,如果放在 (B) 后,那么新加入的数字一定是没加入的数字中最大的(性质3),所以它不会比 (j) 小,转移时使用后缀和优化即可

代码非常简单

#include <cstdio>
const int Mod=1e9+7,N=2005;
int f[N][N],n,k;
int main (){
	scanf ("%d%d",&n,&k);
    for (int i=2;i<=n;i++) f[1][i]=1;
    for (int i=1;i<k-1;i++){
        int sum=f[i][n-i+1];
        for(int j=n-i;j>=2;j--){
            sum=(sum+f[i][j])%Mod;
            f[i+1][j]=(f[i+1][j]+sum)%Mod;
        }
    }
    int ans=0;
    for (int j=2;j<=n-k+2;j++) ans=(ans+f[k-1][j])%Mod;
    if (k==1) ans=1;
    for (int i=1;i<=n-1-k;i++) ans=ans*2%Mod;
    printf ("%d",ans);
	return 0;
}

CF1139D Steps to One

题意:

给定一个数字 (m) ,每次等概率随机一个 ([1,m]) 区间内的数字加到序列中,问序列的 (gcd=1) 的期望步数

(mle 10^5)

题解:

Orz CYJian

(f[i]) 表示 (gcd)(i) 变成 (1) 的期望步数

显然 (f[1]=0) 那么:

[ans=1+frac{sum_{i=1}^mf[i]}{m} ]

考虑转移,显然有:

[f[i]=1+frac{sum_{j=1}^mf[gcd(i,j)]}{m} ]

对式子进行莫比乌斯反演:

[sum_{j=1}^mf[gcd(i,j)]\ sum_{d|i}f[d]sum_{j=1}^m [gcd(i,j)=d]\ sum_{d|i}f[d]sum_{j=1}^{m/d} [gcd(i/d,j)=1]\ sum_{d|i}f[d]sum_{j=1}^{m/d} sum_{t|gcd(i/d,j)}mu(t)\ sum_{d|i}f[d]sum_{t|(i/d)}mu(t)sum_{t|j}^{m/d} 1\ sum_{d|i}f[d]sum_{t|(i/d)}mu(t)lfloorfrac{m}{td} floor\ ]

(T=td) ,继续推:

[sum_{d|i}f[d]sum_{t|(i/d)}mu(t)lfloorfrac{m}{td} floor\ sum_{T|i} lfloorfrac{m}{T} floorsum_{d|T}f[d]mu(frac{T}{d})\ ]

故原式为:

[f[i]=1+frac{sum_{T|i} lfloorfrac{m}{T} floorsum_{d|T}f[d]mu(frac{T}{d})}{m} ]

(g[T]=sum_{d|T}f[d]mu(frac{T}{d}))

对于每一个新递推出的 (f[x]) ,可以以调和级数的复杂度更新所有的 (g)

但是当 (T=d=i) 的时候会出现未知的 (f[i]) ,考虑把它拿到等式左边:

[f[i]=1+frac{sum_{T|i} lfloorfrac{m}{T} floorsum_{d|T}f[d]mu(frac{T}{d})[d ot=i]}{m}+frac{f[i]lfloorfrac{m}{i} floor}{m}\ m imes f[i]=m+sum_{T|i} lfloorfrac{m}{T} floorsum_{d|T}f[d]mu(frac{T}{d})[d ot=i]+f[i]lfloorfrac{m}{i} floor\ {(m-lfloorfrac{m}{i} floor}) imes f[i]=m+sum_{T|i} lfloorfrac{m}{T} floorsum_{d|T}f[d]mu(frac{T}{d})[d ot=i]\ f[i]=frac{m+sum_{T|i} lfloorfrac{m}{T} floorsum_{d|T}f[d]mu(frac{T}{d})[d ot=i]}{{m-lfloorfrac{m}{i} floor}}\ ]

这样就可以转移了......时间复杂度 (O(mlogm))

#include <bits/stdc++.h>
using namespace std;
const int N=100005,Mod=1e9+7;
bool notp[N];int p[N],tot=0,mu[N];
vector <int > d[N];
int f[N],g[N];
int m;
inline void getmu(){
	mu[1]=1;
	for (int i=2;i<=m;i++){
		if (!notp[i]) p[++tot]=i,mu[i]=-1;
		for (int j=1;j<=tot&&i*p[j]<=m;j++){
			notp[i*p[j]]=true;
			if (i%p[j]==0) break;
			mu[i*p[j]]=-mu[i];
		}
	}
	for (int i=1;i<=m;i++)
		for (int j=i;j<=m;j+=i)
			d[j].push_back(i);
}
inline void insert(int x){
	for (int i=1;i*x<=m;i++)
		g[i*x]=((g[i*x]+f[x]*mu[i])%Mod+Mod)%Mod;
}
inline int qpow(int a,int b){
	int ans=1;
	while (b){
		if (b&1) ans=1ll*ans*a%Mod;
		a=1ll*a*a%Mod,b>>=1;
	}
	return ans;
}
int main (){
	scanf ("%d",&m);
	getmu();
	f[1]=0;
	int ans=0;
	for (int i=2;i<=m;i++){
		f[i]=m;int s=d[i].size();
		for (int j=0;j<s;j++){
			int T=d[i][j];
			f[i]=(f[i]+1ll*(m/T)*g[T]%Mod)%Mod;
		}
		f[i]=1ll*f[i]*qpow(m-(m/i),Mod-2)%Mod;
		ans=(ans+f[i])%Mod;insert(i);
	}
	ans=1ll*ans*qpow(m,Mod-2)%Mod,ans=(ans+1)%Mod;
	printf ("%d",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/crazyzh/p/10477780.html