牛客CSP-S提高组赛前集训营4 赛后总结

复读数组

分成 3 种区间算答案:

  1. 一个块内的区间
  2. 两个块交界处,长度小于块长的区间
  3. 长度不小于块长的区间

对于第三种区间,容易发现每个区间的权值一样,只需要算出个数即可.

对于前两种空间,我的思路是:对于一个重复出现的元素,记第一次出现的这个元素贡献权值,然后讨论每一个数会给哪些区间贡献权值即可.

3年OI一场空

不开long long见祖宗

代码:

#include<bits/stdc++.h>
#define LL long long 
#define int long long 
const int SIZE=200005,Mod=1000000007;
int n,k,Raw[SIZE],A[SIZE],Tot;
int pre[SIZE],nex[SIZE],pos[SIZE];
bool mk[SIZE];

long long Ans=0;
LL Pow(LL B,int P)
{
	LL x=1;
	for(;P;P>>=1)
	{
		if(P&1)x=(x*B)%Mod;
		B=(B*B)%Mod;
	}
	return x;
}
LL Inv(LL x)
{
	return Pow(x,Mod-2);
}
LL Sigma(LL L,LL R)
{
	return (L+R)%Mod*(R-L+1+Mod)%Mod*Inv(2)%Mod;
}

signed main()
{
	scanf("%lld%lld",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&A[i]);
		Raw[i]=A[i];
	}
	std::sort(Raw+1,Raw+1+n);
	Tot=std::unique(Raw+1,Raw+1+n)-(Raw+1);
	/*-------------块内-------------*/
	for(int i=1;i<=n;i++)
	{
		A[i]=std::lower_bound(Raw+1,Raw+1+Tot,A[i])-Raw;
		if(!mk[A[i]])mk[A[i]]=1;
		else
		{
			nex[pos[A[i]]]=i;
			pre[i]=pos[A[i]];
		}
		pos[A[i]]=i;
	}
	for(int i=1;i<=n;i++)
		if(nex[i]==0)
			nex[i]=n+1;
	for(int i=1;i<=n;i++)
	{
		Ans+=1LL*(i-pre[i])*(n+1-i);
		Ans%=Mod;
	}
	Ans=(Ans-Tot+Mod)%Mod;
	LL Rem=Ans;
	Ans=Ans*k%Mod;
	/*-------------长度不小于整块的区间-------------*/
	LL num=Sigma(1,(n*k-n+1)%Mod);
	Ans=(Ans+num*Tot)%Mod;
	/*-------------块与块交界处-------------*/
	if(k>1)
	{
		for(int i=n+1;i<=2*n;i++)
			A[i]=A[i-n];
		memset(pre,0,sizeof(pre));
		memset(nex,0,sizeof(nex));
		memset(mk,0,sizeof(mk));
		memset(pos,0,sizeof(pos));
		for(int i=1;i<=2*n;i++)
		{
			if(!mk[A[i]])mk[A[i]]=1;
			else
			{
				nex[pos[A[i]]]=i;
				pre[i]=pos[A[i]];
			}
			pos[A[i]]=i;
		}
		for(int i=1;i<=2*n;i++)
			if(nex[i]==0)
				nex[i]=2*n+1;
		LL Tem=0;
		for(int i=1;i<=2*n;i++)
		{
			Tem+=1LL*(i-pre[i])*(2*n+1-i);
			Tem%=Mod;
		}
		Tem-=2*Rem;
		Tem-=Sigma(1,n*2-n+1)*Tot;
		Tem=((Tem%Mod)+Mod)%Mod;
		Ans+=Tem*(k-1);		
	}
	printf("%lld",Ans%Mod);
	return 0;
}

路径计数机

考虑把问题转化为"给定树上若干条链,求有多少对链相交".

不妨令\(F(S)\)表示\(S\)这个链的集合中有多少对链相交.

这个问题可以根据树上两链相交,一定有一条链两端点的LCA在另一条链上这个性质来求解.

考虑集合\(A\)包含所有长度为\(p\)的链,集合\(B\)包含所有长度为\(q\)的链.那么答案为\(F(A\or B)-(F(A)+F(B))\)

树链剖分维护,时间复杂度\(O(n^2\log^2 n)\).

#include<bits/stdc++.h>
#define LL long long
#define int long long
const int SIZE=6005;

int In()
{
    char ch=getchar();
    int x=0;
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x;
}

int n,p,q,head[SIZE],nex[SIZE],to[SIZE],Tot;
int son[SIZE],Siz[SIZE],Dep[SIZE],F[SIZE],ID[SIZE],Top[SIZE],new_node;

struct Tree
{
	int sum[SIZE*4],Tag[SIZE*4];
	#define LC(x) (x<<1)
	#define RC(x) (x<<1|1)
	#define Mid ((L+R)>>1)
	void push_up(int x){sum[x]=sum[LC(x)]+sum[RC(x)];}
	void Do(int x,int L,int R,int K)
	{
		sum[x]+=(R-L+1)*K;
		Tag[x]+=K;
	}
	void push_down(int x,int L,int R)
	{
		if(Tag[x])
		{
			Do(LC(x),L,Mid,Tag[x]);
			Do(RC(x),Mid+1,R,Tag[x]);
			Tag[x]=0;
		}
	}
	void Change(int x,int L,int R,int X,int Y,int K)
	{
		if(L>Y||R<X)return;
		if(L>=X&&R<=Y)
		{
			Do(x,L,R,K);
			return;
		}
		push_down(x,L,R);
		Change(LC(x),L,Mid,X,Y,K);
		Change(RC(x),Mid+1,R,X,Y,K);
		push_up(x);
	}
	int Query(int x,int L,int R,int X,int Y)
	{
		if(L>Y||R<X)return 0;
		if(L>=X&&R<=Y)return sum[x];
		push_down(x,L,R);
		return Query(LC(x),L,Mid,X,Y)+Query(RC(x),Mid+1,R,X,Y);
	}
}T;

void Link(int u,int v)
{
	nex[++Tot]=head[u];head[u]=Tot;to[Tot]=v;
	nex[++Tot]=head[v];head[v]=Tot;to[Tot]=u;
}

void DFS1(int u)
{
	Siz[u]=1;
	for(int i=head[u];i;i=nex[i])
	{
		int v=to[i];
		if(Dep[v])continue;
		Dep[v]=Dep[u]+1;
		F[v]=u;
		DFS1(v);
		Siz[u]+=Siz[v];
		if(Siz[v]>Siz[son[u]])
			son[u]=v;
	}
}

void DFS2(int u,int TOP)
{
	ID[u]=++new_node;
	Top[u]=TOP;
	if(Siz[u]==1)return;
	DFS2(son[u],TOP);
	for(int i=head[u];i;i=nex[i])
	{
		int v=to[i];
		if(v==F[u]||v==son[u])continue;
		DFS2(v,v);
	}
}

void mk(int u,int v,int K)
{
	while(Top[u]!=Top[v])
	{
		if(Dep[Top[u]]<Dep[Top[v]])
			std::swap(u,v);
		T.Change(1,1,n,ID[Top[u]],ID[u],K);
		u=F[Top[u]];
	}
	if(Dep[u]<Dep[v])
		std::swap(u,v);
	T.Change(1,1,n,ID[v],ID[u],K);
}

int sum(int u,int v)
{
	int x=0;
	while(Top[u]!=Top[v])
	{
		if(Dep[Top[u]]<Dep[Top[v]])
			std::swap(u,v);
		x+=T.Query(1,1,n,ID[Top[u]],ID[u]);
		u=F[Top[u]];
	}
	if(Dep[u]<Dep[v])
		std::swap(u,v);
	x+=T.Query(1,1,n,ID[v],ID[u]);
	return x;	
}

int LCA(int u,int v)
{
	while(Top[u]!=Top[v])
	{
		if(Dep[Top[u]]<Dep[Top[v]])
			std::swap(u,v);
		u=F[Top[u]];
	}
	if(Dep[u]>Dep[v])
		std::swap(u,v);
	return u;
}

int Dis(int u,int v,int L)
{
	return Dep[u]+Dep[v]-2*Dep[L];
}

struct node
{
	int u,v,L;
}G[9000005];
int A[9000005],B[9000005],C,C1,C2;

signed main()
{
	scanf("%lld%lld%lld",&n,&p,&q);
	int u,v;
	for(int i=1;i<n;i++)
	{
		scanf("%lld%lld",&u,&v);
		Link(u,v);
	}
	Dep[1]=1;
	DFS1(1);
	DFS2(1,1);
	for(int u=1;u<=n;u++)
		for(int v=1;v<u;v++)
		{
			int L=LCA(u,v);
			G[++C]=(node){u,v,L};
			if(Dis(u,v,L)==p)
			{
				A[++C1]=C;
			}
			if(Dis(u,v,L)==q)
			{
				B[++C2]=C;
			}
		}
	LL Ans=0,Ans1=0,Ans2=0;
	/**************************************/
	for(int i=1;i<=C1;i++)
		mk(G[A[i]].L,G[A[i]].L,1);
	for(int i=1;i<=C1;i++)
		Ans1+=sum(G[A[i]].u,G[A[i]].v);
	for(int i=1;i<=n;i++)
	{
		int Tem=sum(i,i);
		Ans1-=Tem*(Tem-1)/2;
	}
	for(int i=1;i<=C1;i++)
		mk(G[A[i]].L,G[A[i]].L,-1);	
	/**************************************/
	for(int i=1;i<=C2;i++)
		mk(G[B[i]].L,G[B[i]].L,1);
	for(int i=1;i<=C2;i++)
		Ans2+=sum(G[B[i]].u,G[B[i]].v);
	for(int i=1;i<=n;i++)
	{
		int Tem=sum(i,i);
		Ans2-=Tem*(Tem-1)/2;
	}
	/**************************************/
	for(int i=1;i<=C1;i++)
		mk(G[A[i]].L,G[A[i]].L,1);
	for(int i=1;i<=C1;i++)
		Ans+=sum(G[A[i]].u,G[A[i]].v);	
	for(int i=1;i<=C2;i++)
		Ans+=sum(G[B[i]].u,G[B[i]].v);	
	for(int i=1;i<=n;i++)
	{
		int Tem=sum(i,i);
		Ans-=1LL*Tem*(Tem-1)/2;
	}
	Ans=Ans-Ans1-Ans2;
	Ans=1LL*C1*C2-Ans;
	Ans*=4;
	printf("%lld",Ans);
	return 0;
}

排列计数机

略.

posted @ 2019-11-08 11:33  TaylorSwift13  阅读(191)  评论(0编辑  收藏  举报