杂题

AtCoder AGC028 D

2 n 2n 2n 个点围成一圈,顺时针标号 1 … 2 n 1\dots 2n 12n。要把这些点份分成 n n n 对,每对之间用线段相连,如果两条线段相交,那么四个端点联通。初始给定 k k k 对已经配对的点,求所有配对方式的联通块总数。
n ≤ 300 n\le 300 n300

思路是计算每个(种)联通块出现的方案数。

设一个联通块最小的点是 i i i,最大的点是 j j j,发现每一个联通块都能被这么表示一遍。我们尝试这样计算每一个联通块出现的方案数。令 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示只考虑配对 i i i j j j 中的点, i i i 所在的联通块的最大编号是 j j j 的方案数。如果 i i i j j j 中间有初始配好的点连向了外面,那么方案数为 0。否则用随便配对的方案数减去不合法的。设区间中未配对点数为 s u m sum sum,总方案数为 ( s u m − 1 ) × ( s u m − 3 ) × ⋯ × 1 (sum-1)\times(sum-3)\times\cdots \times1 (sum1)×(sum3)××1。对于不合法的方案,枚举 i i i 所在联通块的最大值为 k k k,要减去 d p [ i ] [ k ] dp[i][k] dp[i][k] 乘上 k + 1 , ⋯   , j k+1,\cdots ,j k+1,,j 中随便配的方案数。

#include
#define ll long long
using namespace std;
const int mod=1e9+7;
ll f[1010],dp[1010][1010];
int s[1010],vis[1010],lk[1010];
inline int read()
{
	char c=getchar();int x=0,flag=1;
	while(!isdigit(c)){if(c=='-') flag=-1;c=getchar();}
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	return x*flag;
}
int dfs(int l,int r)
{
	//cout<=r||(s[r]-s[l-1])&1) return 0;
	if(~dp[l][r]) return dp[l][r];
	for(int i=l;i<=r;i++) if(vis[i]&&(lk[i]>r||lk[i]

Codeforces 1063F

给定一个字符串,在其中选择若干个不相交子串,满足后一个子串的长度严格小于前一个,并且是前一个子串的子串。最大化选出子串的数量。

观察到答案是 n \sqrt n n 级别的,并且一定存在一个最优解的长度是从1开始的连续整数。

sol1:
f [ i ] [ j ] f[i][j] f[i][j] 表示第一个串从 i i i 开始,往后面选 j j j 个串可不可行。然后在外层枚举 j j j,hash 一下就可以从后往前转移。

sol2:
f [ i ] f[i] f[i] 表示第一个串从 i i i 开始,往后最多选多少个串。还是从后往前转移,显然可以二分一个答案 m i d mid mid,查询 x > i + m i d − 1 x>i+mid-1 x>i+mid1 并且 l c p ( i , x ) ≥ m i d lcp(i,x)\ge mid lcp(i,x)mid f f f 值最大的 x x x,判断 f [ x ] f[x] f[x] 是否大于等于 m i d − 1 mid-1 mid1。这样就有了一个 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n) 的做法。

然后类似后缀数组求 h e i g h t height height,可以发现 f [ i + 1 ] ≥ f [ i ] − 1 f[i+1]\ge f[i]-1 f[i+1]f[i]1。因此 f [ i ] ≤ f [ i + 1 ] + 1 f[i]\le f[i+1]+1 f[i]f[i+1]+1,可以在均摊 O ( n ) O(n) O(n) 的时间内完成计算。

具体的,我们如何 check 一个答案 x x x?实际上我们是要求原序列位置在某个后缀,并且 rank 序列位置在某个区间中的 f f f 的最大值。这是一个二维问题,并且有一维是一个后缀形式,这样就可以用主席树来做。主席树外面的维度是原序列的位置。

#include
#define ll long long
using namespace std;
int f[2000010][20],Max[20000010],root[500010],ls[20000010],rs[20000010],sa[500010],wb[500010],x[500010],y[500010],rk[500010],gg[500010],n,height[500010],tot;
char s[500010];
inline int read()
{
	char c=getchar();int x=0,flag=1;
	while(!isdigit(c)){if(c=='-') flag=-1;c=getchar();}
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	return x*flag;
}
void SA()
{
	int m=1000,p=0;
	for(int i=1;i<=n;i++) wb[x[i]=s[i]]++;
	for(int i=1;i<=m;i++) wb[i]+=wb[i-1];
	for(int i=n;i>=1;i--) sa[wb[x[i]]--]=i;
	for(int j=1;pj) y[++p]=sa[i]-j;
		for(int i=1;i<=m;i++) wb[i]=0;
		for(int i=1;i<=n;i++) wb[x[y[i]]]++;
		for(int i=1;i<=m;i++) wb[i]+=wb[i-1];
		for(int i=n;i>=1;i--) sa[wb[x[y[i]]]--]=y[i];
		p=1;
		for(int i=1;i<=n;i++) swap(x[i],y[i]);
		x[sa[1]]=1;
		for(int i=2;i<=n;i++)
		{
			if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j]) x[sa[i]]=p;
			else x[sa[i]]=++p;
		}
	}
	for(int i=1;i<=n;i++) rk[sa[i]]=i;
    int k=0;
    for(int i=1;i<=n;i++)
    {
        if(k) k--;
        if(rk[i]==1) continue;
        int j=sa[rk[i]-1];
        while(s[j+k]==s[i+k]) k++;
        height[rk[i]]=k;
    }
    memset(f,0x3f,sizeof(f));
	for(int i=1;i<=n;i++) f[i][0]=height[i];

	for(int j=1;j<=19;j++)
		for(int i=1;i<=n;i++) f[i][j]=min(f[i][j-1],f[i+(1<r) return 1e9;
	int k=gg[r-l+1];
	return min(f[l][k],f[r-(1<>1;
	if(x<=mid) modify(ls[root],ls[pre],l,mid,x,k),rs[root]=rs[pre];
	else modify(rs[root],rs[pre],mid+1,r,x,k),ls[root]=ls[pre];
}
int query(int root,int l,int r,int x,int y)
{
	if(!root) return 0;	
	if(x>y) return 0;
	if(x<=l&&y>=r) return Max[root];
	int mid=l+r>>1,ans=0;
	if(x<=mid) ans=query(ls[root],l,mid,x,y);
	if(y>mid) ans=max(ans,query(rs[root],mid+1,r,x,y));
	return ans;
}
bool check(int xx,int k,int pos)
{
	int x=rk[xx];
	int l=x,r=n,ansl=0,ansr=n+1;
	while(l<=r)
	{
		int mid=l+r>>1;
		if(get(x+1,mid)>1;
		if(get(mid+1,x)=k;
}
int main()
{
	n=read();
	scanf("%s",s+1);
	for(int i=2;i<=n;i++) gg[i]=gg[i>>1]+1;
	SA();
	int k=0,ans=0;
	Max[0]=0;
	for(int i=n;i>=1;i--)
	{
		if(k

ZROI #399

YJC最近在学习竞赛图,竞赛图指任意两个点之间有且仅有一条有向边的有向图。他现在对竞赛图中强连通分量的大小很感兴趣。
YJC定义了一张竞赛图的权值。设1号点所在的强连通分量大小为k,则这张竞赛图的权值为 d k d_k dk d d d 是一个给定的数组。
现在YJC想知道,对于所有 1 ≤ i ≤ n 1≤i≤n 1in,等概率随机生成一张i个点的竞赛图,这张竞赛图的权值的期望是多少?YJC发现自己不会算,所以他来向你求助。

竞赛图的性质:
1.竞赛图一定存在哈密顿路径。
2.如果竞赛图强连通,那么一定存在哈密顿回路。
3.竞赛图缩点以后是一条链(可能有跨越的边,但不会有分叉)。

这个题我们需要利用第三个性质进行计算。

g [ n ] g[n] g[n] 表示 n n n 个点的强连通竞赛图的数量。那么我们根据第三条性质枚举缩点后链上最后一个点:
g [ n ] = 2 n ( n − 1 ) 2 − ∑ i = 1 n − 1 ( n i ) g [ i ] ⋅ 2 ( n − i ) ( n − i − 1 ) 2 g[n]=2^{\frac{n(n-1)}2}-\sum_{i=1}^{n-1}{n\choose i}g[i]·2^{\frac {(n-i)(n-i-1)}2} g[n]=22n(n1)i=1n1(in)g[i]22(ni)(ni1)

由于每个点等价,我们求出所有图的每个点的点权和,除以 n n n 就是答案。设 f [ n ] f[n] f[n] 表示 n n n 个点的所有图的每个点的点权和,我们枚举最后一个点的大小:
f [ n ] = ∑ i = 1 n ( n i ) g [ i ] ( f [ n − i ] + d [ i ] ⋅ i ⋅ 2 ( n − i ) ( n − i − 1 ) 2 ) f[n]=\sum_{i=1}^{n}{n\choose i}g[i]\bigg(f[n-i]+d[i]·i·2^{\frac{(n-i)(n-i-1)}2}\bigg) f[n]=i=1n(in)g[i](f[ni]+d[i]i22(ni)(ni1))

你可能感兴趣的:(杂题)