zoukankan      html  css  js  c++  java
  • 牛客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(Aor B)-(F(A)+F(B)))

    树链剖分维护,时间复杂度(O(n^2log^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;
    }
    

    排列计数机

    略.

  • 相关阅读:
    Hbase实用技巧:全量+增量数据的迁移方法
    求职时这样回答问题你就输了!来自IT类面试官视角的深度解读
    云原生2.0时代:开启应用定义基础设施新时代
    让“物”能说会道,揭晓华为云IOT黑科技
    API生态的发展与机遇:从5000组数据看中国API生态与开发者现状
    如何实现微服务架构下的分布式事务?
    Win32可执行文件的开发过程 Win32汇编语言008
    鱼C加密程序 零基础入门学习Delphi10
    Win32可执行文件的开发过程 Win32汇编语言008
    使用MASM01 Win32汇编语言009
  • 原文地址:https://www.cnblogs.com/TaylorSwift13/p/11819084.html
Copyright © 2011-2022 走看看