[HAOI2017]供给侧改革

题目描述

这里写图片描述

做法

lcp长度不超过t,这个概率为1-(1-1/2^t)^(n^2)。
t可以取40。
离线扫描,把询问挂在右端点。
每一个位置把从它开始后长度为40的字符串丢入trie中。
可以在trie的每一个结点保留当前子树中位置最大的两个。
然后可以线段树维护一发。
每次区间max标记可以考虑暴力,均摊复杂度是对的。
你可能不知道我在说什么因为我说的很不详细。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=100000+10,maxtot=maxn*40;
int tree[maxn*4],mi[maxn*4],s[maxn];
int g[maxtot][2],fi[maxtot],se[maxtot];
int h[maxn],go[maxn],next[maxn],ask[maxn],ans[maxn],sta[80];
int i,j,k,l,r,t,n,m,tot,top,cnt,lim,root;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
char get(){
    char ch=getchar();
    while (ch!='0'&&ch!='1') ch=getchar();
    return ch;
}
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void change(int p,int l,int r,int a,int b,int v){
    if (mi[p]>=v) return;
    if (l==r){
        tree[p]=mi[p]=v;
        return;
    }
    if (l==a&&r==b){
        int mid=(l+r)/2;
        change(p*2,l,mid,a,mid,v);
        change(p*2+1,mid+1,r,mid+1,b,v);
        tree[p]=tree[p*2]+tree[p*2+1];
        mi[p]=min(mi[p*2],mi[p*2+1]);
        return;
    }
    int mid=(l+r)/2;
    if (b<=mid) change(p*2,l,mid,a,b,v);
    else if (a>mid) change(p*2+1,mid+1,r,a,b,v);
    else{
        change(p*2,l,mid,a,mid,v);
        change(p*2+1,mid+1,r,mid+1,b,v);
    }
    tree[p]=tree[p*2]+tree[p*2+1];
    mi[p]=min(mi[p*2],mi[p*2+1]);
}
int query(int p,int l,int r,int a,int b){
    if (l==a&&r==b) return tree[p];
    int mid=(l+r)/2;
    if (b<=mid) return query(p*2,l,mid,a,b);
    else if (a>mid) return query(p*2+1,mid+1,r,a,b);
    else return query(p*2,l,mid,a,mid)+query(p*2+1,mid+1,r,mid+1,b);
}
void insert(int &x,int l,int r,int y){
    if (!x) x=++tot;
    if (!fi[x]) fi[x]=i;
    else{
        se[x]=fi[x];
        fi[x]=i;
        change(1,1,n,1,se[x],y);
    }
    if (l>r) return;
    insert(g[x][s[l]],l+1,r,y+1);
}
void write(int x){
    if (!x){
        putchar('0');
        putchar('\n');
        return;
    }
    top=0;
    while (x){
        sta[++top]=x%10;
        x/=10;
    }
    while (top) putchar('0'+sta[top--]);
    putchar('\n');
}
int main(){
    freopen("b.in","r",stdin);freopen("b.out","w",stdout);
    n=read();m=read();
    fo(i,1,n) s[i]=get()-'0';
    lim=min(40,n);
    fo(i,1,m){
        l=read();r=read();
        ask[i]=l;
        add(r,i);
    }
    tot=0;
    fo(i,1,n){
        if (i+lim-1>n) t=n;else t=i+lim-1;
        insert(root,i,t,0);
        t=h[i];
        while (t){
            ans[go[t]]=query(1,1,n,ask[go[t]],i);
            t=next[t];
        }
    }
    fo(i,1,m) write(ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值