【线段树】【CDQ分治】回转寿司

来源:互联网 发布:淘宝店主自己做模特 编辑:程序博客网 时间:2024/04/28 22:12

题目大意

给你一个序列,求连续子序列和在【L,R】之间的方案数
N≤100000,|Ai|≤100000,0≤L, R≤109.

分析

这是我打的第一题CDQ(太菜了)
我对这题印象很深刻
当时大家有各种做法
好像都是线段树?
然后这时出现了一股清流
dhr的CDQ分治(orz dhr 好短啊)
然后愉悦的改完后就没管了

很久以后yzx讲CDQ分治时想到我曾经打过一道

先递归再更新答案然后sort回溯
#include<cstdio>#include<iostream>#include<algorithm>using namespace std;typedef long long LL;int n,l,r,a[100050];LL s[100050],ans;void solve(int p,int q){    if(p>=q)return;    int mid=(p+q)/2;    solve(p,mid); solve(mid+1,q);    int u=p,v=p;    for(int i=mid+1;i<=q;i++){        while(u<=mid&&s[i]-s[u]>=l)u++;        while(v<=mid&&s[i]-s[v]>r)v++;        ans+=max(u-v,0);    }    sort(s+p,s+q+1);}int main(){    scanf("%d%d%d",&n,&l,&r);    for(int i=1;i<=n;i++){        scanf("%d",&a[i]);        s[i]=a[i]+s[i-1];    }    solve(0,n);    printf("%lld",ans);}

现在再补上一种值域线段树的做法
对于前缀和s【i】(i表示以i为末尾)
满足

l<=sjsi<=ri<j

sjr<=si<=sjl

将s【i】插入一颗值域线段树(初始化时插入0)
对于每个s【j】查询值域在[s【j】-l,s【j】-r]之间的s【i】个数

#include<cstdio>#define INF 10000000000#define N 100010#define mid ((l+r)>>1)using namespace std;typedef long long LL;LL ans,sum,L,R;int n,u,v,top,p,t[N*40],lc[N*40],rc[N*40],rt;void Ins(int &x,LL l,LL r){    if(!x)x=++top;    t[x]++;    if(l==r)return;    if(sum<=mid)Ins(lc[x],l,mid);    else Ins(rc[x],mid+1,r);}int Que(int x,LL l,LL r){    if(!x)return 0;    if(L<=l&&r<=R)return t[x];    int ans=0;    if(L<=mid)ans+=Que(lc[x],l,mid);    if(R>mid)ans+=Que(rc[x],mid+1,r);    return ans;}int main(){    freopen("data.txt","r",stdin);    scanf("%d%d%d",&n,&u,&v);    Ins(rt,-INF,INF);    for(int i=1;i<=n;i++){        scanf("%d",&p);        sum+=p;        L=sum-v,R=sum-u;        ans+=Que(rt,-INF,INF);        Ins(rt,-INF,INF);    }    printf("%lld\n",ans);}

还有一种以i为首的树状数组
链接
感觉二分可以换成two pointer
或者可以看Orz 曾老师的代码

#include<cstdio>#include<algorithm>#define ll long longusing namespace std;inline ll rd(){    ll c, r=0, s=1;    while((c=getchar())<48||57<c) if(c=='-') s=-1;    while(c>47&&58>c) r=10*r+c-48, c=getchar();    return s*r;}#define mxn 100233ll n, l, r, i, j;pair<ll, ll> a[mxn];#define fir(i) a[i].first#define sec(i) a[i].second//第i小的前缀的值与序号 ll b[mxn];inline void add(ll i, ll x) {for(;i<n+2; i+=i&-i) b[i]+=x;}inline ll sum(ll i) {ll r=0; for(;i; i-=i&-i) r+=b[i]; return r;}ll ans;inline ll solve(ll m){    if(m<0) return 0; ans=0;    for(i=1; i<n+2; i++) b[i]=0;    for(i=j=1; i<n+1; add(sec(++i), -1))    {        for(;j<n+1&&fir(j+1)<=fir(i)+m; add(sec(++j), 1));        ans+=j-i-sum(sec(i));    }    return ans;}inline void debug(){    for(ll ii=0; ii<=fir(n+1); ii++) printf("%I64d %I64d\n", ii, solve(ii));}int main(){    n=rd(); l=rd(); r=rd(); sec(1)=1;    for(i=2; i<=n+1; i++) a[i]=make_pair(fir(i-1)+rd(), i);    sort(a+1, a+n+2);    printf("%lld\n", solve(r)-solve(l-1));    //debug();    return 0;}
原创粉丝点击