zoukankan      html  css  js  c++  java
  • luogu P4384 [八省联考2018]制胡窜

    luogu

    先特判(l=r)的答案为(inom{n-1}{2}).这里的枚举(i,j)等价于把原串分成三个非空段,然后题目要求的是(s_{l,r})至少在一个段中出现,不妨考虑求总方案数(inom{n-1}{2})减去(s_{l,r})不在任何一段中出现的方案

    把原串分成三个非空段等价于选择两个位置(i<j<n),在(i)(i+1)之间,(j)(j+1)之间画竖线(后面称在(i)(i+1)之间的竖线为在(i)位置的竖线),那么要使得(s_{l,r})不在整段中出现,相当于对于所有(k)满足(s_{k-(r-l),k}=s_{l,r}),存在一条在([k-(r-l),k-1])之间的竖线.所以把(s_{l,r})结束位置集合(endpos)扣出来,现在问题变成有若干个长度为(len=r-l)的区间,要选择两个位置,使得所有区间至少包含两个位置中的一个

    后面记(pl,pr)分别为(s_{l,r})(endpos)最小和最大的位置.对于这个问题,可以分两种情况.第一种是存在一个位置经过所有区间的交,那么只需要知道这个交的大小(a=(pl-1)-(pr-len))就可以简单算出答案了,大概为((n-2)a-inom{a}{2})

    第二种就相当于是先后选择两个位置(i,j),使得可以把区间分成两个集合,并且分别包含一个位置,这里会有(jin[max(pl,pr-len),min j(jin endpos(s_{l,r}),jge pl+ln)]),前者是因为你不能把(j)设为前面在所有区间的交中统计过的位置,后者是你必须包含 最左边的 与最左边区间没有交 的区间

    (j)的合法区间左右端点为(ql,qr).然后如果从左往右枚举(j),可选的(i)的数量是不增的,并且可选(i)数量相同的(j)会构成若干区间废话,具体的,设(endpos(s_{l,r}))中在([ql,qr])中有元素(j_1,j_2...j_m),那么如果(jin[j_k,j_{k+1})),可选的(i)个数都为(pl+len-j_k),由于(pl+len)是常量,所以利用线段树维护出区间([j_1,j_{k+1}))这些位置的(sum j_k)贡献

    然后还剩([ql,j_1)),([j_{k+1},qr])这两个区间,可以发现找到([pl,ql))中在最大的(endpos)的元素(j_0)后,两个区间中每个位置可选的(i)个数分别为(pl+len-j_0,pl+len-j_m).注意这里(i)范围会包含前面算过的所有区间交范围,所以还要减去((qr-ql+1)a).还有一种情况是(endpos(s_{l,r}))没有元素在([ql,qr])中,这时直接给总方案减去((qr-ql+1)(pl+len-j_0)),因为这时所有区间交显然为空

    具体细节见代码

    #include<bits/stdc++.h>
    #define LL long long
    #define uLL unsigned long long
    #define db double
    
    using namespace std;
    const int N=2e5+10;
    int rd()
    {
        int x=0,w=1;char ch=0;
        while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=getchar();}
        return x*w;
    }
    char cc[N];
    int n,q,lz,rt[N];
    struct node
    {
    	int l,r;
    	LL s;
    	node(){l=N+1,r=0,s=0;}
    	node operator + (const node &bb) const
    	{
    		node an;
    		an.l=min(l,bb.l),an.r=max(r,bb.r),an.s=s+bb.s;
    		if(l<=r&&bb.l<=bb.r&&r<bb.l) an.s+=1ll*r*(bb.l-r);
    		return an;
    	}
    }s[N*50];
    namespace smr
    {
    	int ch[N*50][2],tt;
    	void inst(int o,int x)
    	{
    		s[o].l=s[o].r=x;
    		int l=1,r=n;
    		while(l<r)
    		{
    			int mid=(l+r)>>1;
    			if(x<=mid) o=ch[o][0]=++tt,r=mid;
    			else o=ch[o][1]=++tt,l=mid+1;
    			s[o].l=s[o].r=x;
    		}
    	}
    	int merg(int o1,int o2)
    	{
    		if(!o1||!o2) return o1+o2;
    		int o=++tt;
    		ch[o][0]=merg(ch[o1][0],ch[o2][0]);
    		ch[o][1]=merg(ch[o1][1],ch[o2][1]);
    		s[o]=s[ch[o][0]]+s[ch[o][1]];
    		return o;
    	}
    	node quer(int o,int l,int r,int ll,int rr)
    	{
    		if(!o||ll>rr) return s[0];
    		if(ll<=l&&r<=rr) return s[o];
    		int mid=(l+r)>>1;
    		if(rr<=mid) return quer(ch[o][0],l,mid,ll,rr);
    		if(ll>mid) return quer(ch[o][1],mid+1,r,ll,rr);
    		return quer(ch[o][0],l,mid,ll,mid)+quer(ch[o][1],mid+1,r,mid+1,rr);
    	}
    }
    namespace sam
    {
    	int to[N],nt[N],hd[N],tot=1;
    	void adde(int x,int y){++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;}
    	int fa[N],ff[N][18],ps[N],de[N],tn[N][10],len[N],tt=1,la=1;
    	void extd(int x,int i)
    	{
    		int np=++tt,p=la;
    		len[np]=len[p]+1,ps[i]=np,smr::inst(rt[np]=++(smr::tt),i),la=np;
    		while(p&&!tn[p][x]) tn[p][x]=np,p=fa[p];
    	    if(!p) fa[np]=1;
    		else
    		{
    			int q=tn[p][x];
    			if(len[q]==len[p]+1) fa[np]=q;
    			else
    			{
    				int nq=++tt;
    				fa[nq]=fa[q],len[nq]=len[p]+1;
    				memcpy(tn[nq],tn[q],sizeof(int)*10),fa[np]=fa[q]=nq;
    				while(p&&tn[p][x]==q) tn[p][x]=nq,p=fa[p];
    			}
     		}
    	}
    	void dfs(int x)
    	{
    		ff[x][0]=fa[x];
    		for(int j=1;j<=lz;++j) ff[x][j]=ff[ff[x][j-1]][j-1];
    		for(int i=hd[x];i;i=nt[i])
    		{
    			int y=to[i];
    			de[y]=de[x]+1,dfs(y),rt[x]=smr::merg(rt[x],rt[y]);
    		}
    	}
    	void inii()
    	{
    		for(int i=1;i<=n;++i) extd(cc[i]-'0',i);
    		for(int i=2;i<=tt;++i) adde(fa[i],i);
    		dfs(1);
    	}
    	void wk()
    	{
    		int zl=rd(),zr=rd(),ln=zr-zl+1,x=ps[zr];
    		LL an=1ll*(n-1)*(n-2)/2;
    		if(zl==zr){printf("%lld
    ",an);return;}
    		for(int j=lz;~j;--j)
    			if(len[ff[x][j]]>=ln) x=ff[x][j];
    		node nw=s[rt[x]];
    		int pl=nw.l,pr=nw.r,y=0;
    		if(pl==pr){printf("%lld
    ",an-(1ll*(n-2)*(ln-1)-1ll*(ln-1)*(ln-2)/2));return;}
    		if(pr-ln+1<=pl-1)
    		{
    			y=(pl-1)-(pr-ln+1)+1;
    			an-=(1ll*(n-2)*y-1ll*y*(y-1)/2);
    		}
    		int ql=max(pl,pr-ln+1),qr=min(pr-1,smr::quer(rt[x],1,n,pl+ln-1,pr).l-1);
    		if(ql>qr){printf("%lld
    ",an);return;}
    		node nx=smr::quer(rt[x],1,n,ql,qr);
    		int v1=pl-(smr::quer(rt[x],1,n,pl,ql).r-ln+1);
    		if(!nx.r){printf("%lld
    ",an-1ll*(qr-ql+1)*v1);return;}
    		an-=1ll*(nx.l-ql)*v1;
    		an-=1ll*(nx.r-nx.l)*(pl+ln-1)-nx.s;
    		int v2=max(pl+ln-1-nx.r,0);
    		an-=1ll*(qr-nx.r+1)*v2;
    		an+=1ll*(qr-ql+1)*y;
    		printf("%lld
    ",an);
    	}
    }
    
    int main()
    {
    	n=rd(),q=rd(),scanf("%s",cc+1),lz=log2(n+n);
    	sam::inii();
    	while(q--) sam::wk();
    	return 0;
    }
    
    
  • 相关阅读:
    Android 杀掉当前程序的进程
    Android Studio 2.3更换默认的ConstraintLayout布局
    Android ConstraintLayout约束控件链接整理
    Java 实现字符串反转
    Android 网络技术HTTP
    数据库框架 Litepal
    Android FastJson解析
    Java类的初始化顺序
    Android 免费短信获取国家列表和国家代码
    【转】仿Android 联系人SideBar排序,根据拼音A-Z字母快速导航,以及输入搜索条件过滤,显示姓名的文字图片
  • 原文地址:https://www.cnblogs.com/smyjr/p/12872464.html
Copyright © 2011-2022 走看看