bzoj3236:[AHOI2013]作业

传送门
这个题网上大多数是使用莫队+树状数组的办法写的,实际上有着更优秀的算法:线段树分治+树状数组
这个题是我在集训队论文上看到的,发现这个思想十分优秀,但是我貌似并不能很好的实现它。
这个做法的思路大概是:首先建一棵权值线段树,对于线段树的每个区间存权值范围在区间内的所有数,再将所有的操作都存入线段树中(分开存),因为权值线段树内的每个区间都限制了权值的范围,然后就可以对于线段树的每个区间去查询位置在\([l,r]\)之间的数有多少,第一问可以随便搞,对于第二问一个简洁的方法就是记下每种权值最后出现的位置,保证它只出现一遍就行了,修改查询用树状数组就可以优秀的解决。
时间复杂度我不会证,论文上写的是\(O((n+Q)log^2n)\)(Q为询问数,n为数的个数)
细节可以看代码(代码实现参考了yww大佬的博客):

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
void read(int &x) {
    char ch; bool ok;
    for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
    for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define max(a,b) (a>b?a:b)
#define rg register
int n,m,num,las[100001],z[100001],ans[1000010],ans1[1000010];
struct oo{int l,r;}s[400001];
struct o{int l,r,a,b,id;}f[100001];
vector<oo>q[300001];
vector<o>g[300001];
struct tree
{
    int w[100001];
    #define lowbit(i) (i&(-i))
    void add(int x,int v){for(int i=x;i<=n;i+=lowbit(i))w[i]+=v;}
    int get(int x){int ans=0;for(int i=x;i;i-=lowbit(i))ans+=w[i];return ans;}
}e1,e2;
void build(int x,int l,int r)
{
    s[x].l=l,s[x].r=r;
    if(l==r){num=max(num,x);return ;}
    int mid=(l+r)>>1;
    build(x<<1,l,mid),build(x<<1|1,mid+1,r);
}
void change(int x,oo y)
{
    q[x].push_back(y);
    if(s[x].l==s[x].r)return ;
    int mid=(s[x].l+s[x].r)>>1;
    if(y.r<=mid)change(x<<1,y);
    else change(x<<1|1,y); 
}
void add(int x,o y)
{
    if(y.a<=s[x].l&&y.b>=s[x].r)
    {
        g[x].push_back(y);
        return ;
    }
    int mid=(s[x].l+s[x].r)>>1;
    if(y.a<=mid)add(x<<1,y);
    if(y.b>mid)add(x<<1|1,y);
}
bool cmp(o a,o b){return a.r<b.r;}
int main()
{
    read(n),read(m),build(1,1,n);
    for(rg int i=1,x;i<=n;i++)read(x),change(1,(oo){i,x});
    for(rg int i=1;i<=m;i++){o now;read(now.l),read(now.r),read(now.a),read(now.b),now.id=i,add(1,now);}
    for(rg int i=1;i<=num;i++)
    {
        int a=q[i].size(),b=g[i].size(),k=0;
        if(!b)continue;
        for(rg int j=1;j<=a;j++)z[j]=q[i][j-1].l;
        sort(g[i].begin(),g[i].end(),cmp);
        while(k<b&&g[i][k].r<z[1])k++;
        for(rg int j=1;j<=a;j++)
        {
            if(las[q[i][j-1].r])e2.add(las[q[i][j-1].r],-1);
            las[q[i][j-1].r]=j;
            e1.add(j,1),e2.add(j,1);
            while(k<b&&(j==a||g[i][k].r<z[j+1]))
            {
                int t=lower_bound(z+1,z+a+1,g[i][k].l)-z;
                ans[g[i][k].id]+=e1.get(j)-e1.get(t-1);
                ans1[g[i][k].id]+=e2.get(j)-e2.get(t-1);
                k++;
            }
        }
        for(rg int j=1;j<=a;j++)
        {
            if(las[q[i][j-1].r])e2.add(las[q[i][j-1].r],-1);
            e1.add(j,-1),las[q[i][j-1].r]=0;
        }
    }
    for(rg int i=1;i<=m;i++)printf("%d %d\n",ans[i],ans1[i]);
}
posted @ 2018-12-06 14:22  蒟蒻--lichenxi  阅读(120)  评论(0编辑  收藏  举报