【loli的胡策】测试4.16(线段树+概率期望dp)

T1

这里写图片描述

题解

仔细分析了题目发现并不是后缀家族的(松口气

然后随便想了一个简单的思路,每次查询的时候遇到’)’就用链表遍历’(‘,然后遍历之间的’w’,至于’0’我们用一个树状数组查看w左边的‘0’个数和右边的‘0’个数乘积就是ans,然而这样的时间复杂度是很不稳妥的 O(n2logn) O ( n 2 l o g n ) ,60pts

然而这种区间修改单点修改我应该想到的———线段树
线段树维护区间内有多少(0w0)
区间内出现的答案分两种:
不跨越中线的:左右子区间答案相加
跨越中线的:分4种:
(0w0在左侧,)在右侧
(0w在左侧,0)在右侧
(0在左侧,w0)在右侧
(在左侧,0w0)在右侧
那么我们就需要维护每个区间内(,(0,(0w,(0w0,0w0),w0),0),)
那我们设f[i][j]表示颜文字i~j这一段在这个区间内出现的次数,那么 f[i][j]=fl[i][j]+fr[i][j]+j1k=i(fl[i][k]+fr[k+1][j]) f [ i ] [ j ] = f l [ i ] [ j ] + f r [ i ] [ j ] + ∑ k = i j − 1 ( f l [ i ] [ k ] + f r [ k + 1 ] [ j ] )
那么时间复杂度为 O(nlogn) O ( n l o g n ) ,还有合并时候的常数,不是很大

代码

60pts

#include <cstdio>
#include <cstring>
#define LL long long
using namespace std;
const LL mod=4294967296LL;
const int N=50005;
int n,num[N],m,nxtz[N],nxtw[N];char st[N];
void add(int loc,int vv){for (int i=loc;i<=n;i+=i&(-i)) num[i]+=vv;}
int qurry(int loc)
{
    int ans=0;
    for (int i=loc;i>=1;i-=i&(-i)) ans+=num[i];
    return ans;
}
int main()
{
    freopen("0w0.in","r",stdin);
    freopen("0w0.out","w",stdout);
    scanf("%d%d",&n,&m);
    scanf("%s",st+1);
    while (m--)
    {
        char w[5],c[5];int x,y;
        scanf("%s",w);scanf("%d",&x);
        if (w[0]=='B')
        {
            scanf("%d",&y);scanf("%s",c);
            for (int i=x;i<=y;i++) st[i]=c[0];
        }else if (w[0]=='A') 
        {
            scanf("%s",c);
            st[x]=c[0]; 
        }else
        {
            scanf("%d",&y);LL ans=0;
            for (int i=1;i<=n;i++) num[i]=0;
            int pointz=0,pointw=0;
            for (int i=x;i<=y;i++) 
            {
                if (st[i]==')')
                  for (int j=pointz;j;j=nxtz[j])
                    for (int k=pointw;k;k=nxtw[k])
                      if (k<j) break;
                      else ans=(ans+(LL)(qurry(k)-qurry(j))*(LL)(qurry(i)-qurry(k))%mod)%mod;       
                if (st[i]=='(') nxtz[i]=pointz,pointz=i;
                if (st[i]=='0') add(i,1);
                if (st[i]=='w') nxtw[i]=pointw,pointw=i;
            }
            printf("%lld\n",ans);
        }
    }
}

100pts
这个模数直接用int自然溢出就行,最后%u

#include <cstdio>
#include <cstring>
#include <iostream>
#define LL long long
using namespace std;
const LL mod=4294967296LL;
const int N=50005;
int n,m,f[N*4][6][6],ans,tmpans;char st[N],delta[N*4];
//( 0 w 0 )
//1 2 3 4 5
int pd(char v)
{
    if (v=='(') return 1;
    if (v=='0') return 2;
    if (v=='w') return 3;
    if (v==')') return 5;
    return 0;
}
void updata(int now,int l,int r)
{
    for (int i=1;i<=5;i++)
      for (int j=i;j<=5;j++)
      {
        f[now][i][j]=f[l][i][j]+f[r][i][j];
        for (int k=i;k<j;k++) f[now][i][j]+=f[l][i][k]*f[r][k+1][j];
      }
}
void pushdown(int now,int l,int r,int mid)
{
    if (delta[now])
    {
        memset(f[now<<1],0,sizeof(f[now<<1]));
        memset(f[now<<1|1],0,sizeof(f[now<<1|1]));
        int sb=pd(delta[now]);
        if (sb==2)
        {
            f[now<<1][2][2]=f[now<<1][4][4]=mid-l+1;
            f[now<<1|1][2][2]=f[now<<1|1][4][4]=r-mid;
        }else 
        {
            f[now<<1][sb][sb]=mid-l+1;
            f[now<<1|1][sb][sb]=r-mid;
        }
        delta[now<<1]=delta[now];
        delta[now<<1|1]=delta[now];
        delta[now]=0;
    }
}
void build(int now,int l,int r)
{
    if (l==r)
    {
        tmpans=max(now,tmpans);
        if (st[l]=='(') f[now][1][1]=1;
        else if (st[l]=='0') f[now][2][2]=f[now][4][4]=1;
        else if (st[l]=='w') f[now][3][3]=1;
        else if (st[l]==')') f[now][5][5]=1;
        return;
    }
    int mid=(l+r)>>1;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    updata(now,now<<1,now<<1|1);
}
void qurry(int now,int l,int r,int lrange,int rrange)
{
    if (lrange<=l && rrange>=r)
    {
        updata(ans,tmpans,now);
        memcpy(f[tmpans],f[ans],sizeof(f[tmpans]));
        return;
    }
    int mid=(l+r)>>1;pushdown(now,l,r,mid);
    if (lrange<=mid) qurry(now<<1,l,mid,lrange,rrange);
    if (rrange>mid) qurry(now<<1|1,mid+1,r,lrange,rrange);
    updata(now,now<<1,now<<1|1);
}
void change(int now,int l,int r,int lrange,int rrange,char v)
{
    if (lrange<=l && rrange>=r)
    {
        delta[now]=v;
        memset(f[now],0,sizeof(f[now]));
        int sb=pd(v); 
        if (sb==2) f[now][2][2]=f[now][4][4]=r-l+1;
        else f[now][sb][sb]=r-l+1;
        return;
    }
    int mid=(l+r)>>1;pushdown(now,l,r,mid);
    if (lrange<=mid) change(now<<1,l,mid,lrange,rrange,v);
    if (rrange>mid) change(now<<1|1,mid+1,r,lrange,rrange,v);
    updata(now,now<<1,now<<1|1);
}
int main()
{
    freopen("0w0.in","r",stdin);
    freopen("0w0.out","w",stdout);
    scanf("%d%d",&n,&m);
    scanf("%s",st+1);
    build(1,1,n);
    ans=0;tmpans++;
    while (m--)
    {
        char w[5],c[5];int x,y;
        scanf("%s",w);scanf("%d",&x);
        if (w[0]=='B')
        {
            scanf("%d",&y);scanf("%s",c);
            change(1,1,n,x,y,c[0]);
        }else if (w[0]=='A') scanf("%s",c),change(1,1,n,x,x,c[0]);
        else
        {
            scanf("%d",&y);
            memset(f[ans],0,sizeof(f[ans]));
            memset(f[tmpans],0,sizeof(f[tmpans]));
            qurry(1,1,n,x,y);
            printf("%u\n",f[ans][1][5]);
        }
    }
}

T2

这里写图片描述
这里写图片描述
这里写图片描述

题解

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值