1. 程式人生 > >簡單的字串(迴文子集數+二維線段樹)

簡單的字串(迴文子集數+二維線段樹)

題目:

思路:第一步先處理如何計算一個區間的迴文子集個數

構成迴文串,有兩種情況: 1. 每種字母都選偶數個,這樣一定可以構成迴文串

                                             2. 有一種字母選了奇數個,其他的選了偶數個,這樣也一定可以構成迴文串

對於每種字母選偶數個或奇數個的選擇方法數,都可以用二項式定理得到2^(cnt-1)。那麼對應於第一種情況,答案應該是

\prod2^(cnt-1)           ,對於第二種情況,應該是C(1,n)*\prod2^(cnt-1)  。最後還要減去空集的情況。

所以最後答案應該是:

第二步就是考慮用二維線段樹去維護區間修改了:

 意思就是維護對26個字母都建立一顆線段樹,每顆都取維護該字母在任意區間的數量,修改的時候注意方向。其他的和普通線段樹類似。

程式碼:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
typedef long long ll;
const int mod=1e9+7;
#define lson rt<<1
#define rson rt<<1|1
#define Lson l,m,lson
#define Rson m+1,r,rson 
struct Tree
{
    int l,r;
    int sum[26],lazy;
}tree[maxn<<2];
char a[maxn];
void push_up(int rt)
{
    for(int i=0;i<26;i++)
    {
        tree[rt].sum[i]=tree[lson].sum[i]+tree[rson].sum[i];
    }
}
void push_down(int rt)
{
    if(tree[rt].lazy==0) return;
    int t=tree[rt].lazy;
    int tmp[26];
    for(int i=0;i<26;i++)
    {
        tmp[i]=tree[lson].sum[i];
    }
    for(int i=0;i<26;i++)
    {
        tree[lson].sum[(i+t)%26]=tmp[i];
    }
    for(int i=0;i<26;i++)
    {
        tmp[i]=tree[rson].sum[i];
    }
    for(int i=0;i<26;i++)
    {
        tree[rson].sum[(i+t)%26]=tmp[i];
    }
    tree[lson].lazy+=t;
    tree[rson].lazy+=t;
    tree[rt].lazy=0;
}
void Build(int l,int r,int rt)
{
    tree[rt].l=l,tree[rt].r=r;
    if(l==r)
    {
        tree[rt].sum[a[l]-'a']++;
        return;
    }
    int m=(l+r)>>1;
    Build(Lson);
    Build(Rson);
    push_up(rt);
}
void updata(int L,int R,int v,int l,int r,int rt)
{
    if(L<=l&&R>=r)
    {
        int tmp[26];
        for(int i=0;i<26;i++)
        {
            tmp[i]=tree[rt].sum[i];
        }
        for(int i=0;i<26;i++)
        {
            tree[rt].sum[(i+v)%26]=tmp[i];
        }
        tree[rt].lazy+=v;
        tree[rt].lazy%=26;
        return;
    }
    push_down(rt);
    int m=(l+r)>>1;
    if(L<=m)
    {
        updata(L,R,v,Lson);
    }
    if(R>m)
    {
        updata(L,R,v,Rson);
    }
    push_up(rt);
}
int query(int i,int L,int R,int l,int r,int rt)
{
   if(L<=l&&R>=r)
   {
       return tree[rt].sum[i];
   }
   push_down(rt);
   int m=(l+r)>>1;
   int ans=0;
   if(L<=m)
   {
       ans+=query(i,L,R,Lson);
   }
   if(R>m)
   {
       ans+=query(i,L,R,Rson);
   }
   push_up(rt);
   return ans;
}
ll fac2[maxn];
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    memset(tree,0,sizeof(tree));
    fac2[0]=1;
    for(int i=1;i<maxn;i++)
    {
        fac2[i]=fac2[i-1]*2%mod;
    }
    int n,q;
    scanf("%d%d",&n,&q);
    scanf("%s",a+1);
    Build(1,n,1);
    int op,l,r,v;
    while(q--)
    {
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d%d%d",&l,&r,&v);
            l++;
            r++;
            v=v%26;
           updata(l,r,v,1,n,1);
        }
        else
        {
            scanf("%d%d",&l,&r);
            l++,r++;
            int s[26];
            int cnt=0;
            ll ans=1;
            for(int i=0;i<26;i++)
            {
                s[i]=query(i,l,r,1,n,1);
                if(s[i])
                {
                    cnt++;
                }
            }
            for(int i=0;i<26;i++)
            {
                if(s[i]==0) continue;
                ans=ans*fac2[s[i]-1]%mod;
            }
            ans=ans*(cnt+1)%mod;
            ans=(ans-1+mod)%mod;
            printf("%d\n",ans);
        }
    }
    return 0;
}