• 牛客练习赛64 如果我让你查回文你还爱我吗 线段树 树状数组 manacher 计数 区间本质不同回文串个数


    LINK:如果我让你查回文你还爱我吗

    了解到了这个模板题. 果然我不会写2333...

    考试的时候想到了一个非常辣鸡的 线段树合并+莫队的做法 过不了不再赘述.

    当然也想到了manacher不过不太会用 所以就自闭了。

    这道题 容易考虑到manacher而不是PAM.

    考虑 在扩充后的字符串上做这个问题 这样就不需要考虑偶数回文串的条件了.

    离线之后右端点不断移动 容易发现一个问题 某个点为中心的回文串个数标记在左端点上不过此时可能左端点够而右端点不太行。

    一个trick 将区间分成两半 这样就没有这个影响了.

    然后查询直接查就行了 可以线段树维护区间加当然也可以树状数组维护.

    值得一提的是 可以发现每次赋值 某一段整体赋值很好做 在奇数回文串的时候这样做容易发现是两倍。

    偶数则不然 这里减掉区间中所有的单独的#就可以发现 答案刚好是二倍了.

    const int MAXN=200010<<1;
    int n,Q;
    struct jl{int l,r;int id;}sl[MAXN],sr[MAXN];
    inline int cmpl(jl a,jl b){return a.r<b.r;}
    inline int cmpr(jl a,jl b){return a.l>b.l;}
    struct wy
    {
    	int l,r;
    	ll sum,tag;
    }t[MAXN<<2];
    char a[MAXN],b[MAXN];
    int p[MAXN];ll ans[MAXN];
    inline void Manacher()
    {
    	b[0]='#';b[1]='$';
    	rep(1,n,i)b[i<<1]=a[i],b[i<<1|1]='$';
    	n=n<<1|1;b[n+1]='0';
    	int mx=0,mid=0;
    	rep(1,n,i)
    	{
    		if(mx>i)p[i]=min(p[(mid<<1)-i],mx-i);
    		else p[i]=1;
    		while(b[i-p[i]]==b[i+p[i]])++p[i];
    		if(p[i]+i>mx)mx=p[i]+i,mid=i;
    	}
    }
    inline void pushdown(int p)
    {
    	int mid=(l(p)+r(p))>>1;
    	sum(zz)+=(ll)tag(p)*(mid-l(p)+1);
    	sum(yy)+=(ll)tag(p)*(r(p)-mid);
    	tag(zz)+=tag(p);tag(yy)+=tag(p);
    	tag(p)=0;return;
    }
    inline void change(int p,int l,int r,int x)
    {
    	if(l<=l(p)&&r>=r(p))
    	{
    		sum(p)+=(ll)(r(p)-l(p)+1)*x;
    		tag(p)+=x;return;
    	}
    	int mid=(l(p)+r(p))>>1;
    	if(tag(p))pushdown(p);
    	if(l<=mid)change(zz,l,r,x);
    	if(r>mid)change(yy,l,r,x);
    	sum(p)=sum(zz)+sum(yy);
    }
    inline ll ask(int p,int l,int r)
    {
    	if(l<=l(p)&&r>=r(p))return sum(p);
    	int mid=(l(p)+r(p))>>1;ll cnt=0;
    	if(tag(p))pushdown(p);
    	if(l<=mid)cnt+=ask(zz,l,r);
    	if(r>mid)cnt+=ask(yy,l,r);
    	return cnt;
    }
    inline void build(int p,int l,int r)
    {
    	l(p)=l;r(p)=r;
    	sum(p)=tag(p)=0;
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	build(zz,l,mid);
    	build(yy,mid+1,r);
    }
    inline void solve_l()
    {
    	build(1,1,n);int flag=1;
    	sort(sl+1,sl+1+Q,cmpl);
    	rep(1,n,i)
    	{
    		change(1,i-p[i]+1,i,1);
    		while(sl[flag].r<=i&&flag<=Q)
    		{
    			ans[sl[flag].id]+=ask(1,sl[flag].l,sl[flag].r);
    			++flag;
    		}
    	}
    }
    inline void solve_r()
    {
    	build(1,1,n);int flag=1;
    	sort(sr+1,sr+1+Q,cmpr);
    	fep(n,1,i)
    	{
    		change(1,i,i+p[i]-1,1);
    		while(sr[flag].l>=i&&flag<=Q)
    		{
    			ans[sr[flag].id]+=ask(1,sr[flag].l,sr[flag].r);
    			++flag;
    		}
    	}
    }
    int main()
    {
    	//freopen("1.in","r",stdin);
    	gt(n);gt(Q);gc(a);
    	Manacher();
    	rep(1,Q,i)
    	{
    		int get(l),get(r);
    		ans[i]-=(r-l+2);
    		l=l<<1;--l;r=r<<1;++r;
    		int mid=(l+r)>>1;
    		sl[i]=(jl){l,mid,i};
    		sr[i]=(jl){mid+1,r,i};
    	}
    	solve_l();solve_r();
    	rep(1,Q,i)putl(ans[i]>>1);
    	return 0;
    }
    
  • 相关阅读:
    【剑指Offer】面试招聘题目2:重建二叉树
    简单的安装部署django程序
    Plinga接入
    游戏设计的一些观点
    开方实现
    Mongodb笔记
    动态编程
    数学基础的算法分析
    简单的统计代码程序
    远程在手机上调试android程序
  • 原文地址:https://www.cnblogs.com/chdy/p/12951403.html
Copyright © 2020-2023  润新知