• 不知道叫什么标题了


    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;
    }
    
  • 相关阅读:
    演示使用Metasploit入侵Windows
    Metasploit的基本使用
    安装使用lynis扫描Linux的安全漏洞
    使用Metasploit收集邮箱信息
    Kali Linux:使用nmap扫描主机
    Kali Linux安装SSH Server
    【转】PHP 无限级分类(递归)
    【转】Mysql only_full_group_by以及其他关于sql_mode原因报错详细解决方案
    ERROR 1366 (HY000): Incorrect string value: 'xADxE5x9BxBDxE9x82...' fo的解决方法
    深入理解HTTP协议
  • 原文地址:https://www.cnblogs.com/crazyzh/p/10477780.html
Copyright © 2020-2023  润新知