18-2-11 刷題心得

本來今天要學fft的,結果還是沒看下去。。。看來達到我智商的瓶頸了QAQ
題目cqoi2011 動態逆序對
比較裸的樹狀數組套線段樹,然而還是卡了我好久。
因爲這題很友好所以不用離散化;
以位置爲主席樹的時間軸,權值爲主席樹上的區間建樹;
那麼每次加一個數的時候的貢獻就是之前出現的數中值比這個數大的個數;
對於修改,套一個樹狀數組,將修改的位置對之後的影響用樹狀數組全部消掉;
對於每次修改產生的影響,需要查詢小於這個位置中所有大於這個數的和,由於樹狀數組只能保證前綴和的性質,對於區間查詢其實可以想普通線段樹那樣進行查詢,但是不怎麼會傳遞數組,就查詢了兩個端點的前綴和再相減;
而查詢大於這個數的位置且小於這個數的和的時候外邊也要有一層差分,即所有位置小於這個數的和減去這個位置之前小於這個數的和;
我也不知道怎麼着就過了這個題,還是多打點註釋吧orz

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#include<cctype>
#define LL long long
#define random(a,b) (a+rand()%(b-a+1))
const int maxn=100005;
int n,m;
int a[maxn];
int too[maxn];
int s[maxn];
int root[maxn];
int ntot=0;
int lc[maxn];
int lowbit(int x)
{
    return x&(-x);
}
struct asd
{
    int l,r,sum;
}t[maxn*100];
int build(int l,int r,int y,int pos,int v)
{
    ntot++; 
    int tmp=ntot;
    t[ntot]=t[y],t[ntot].sum+=v;
    while(l!=r)
    {
        int mid=(l+r)>>1;
        if(pos<=mid)r=mid,y=t[y].l,t[ntot].l=ntot+1;
        else l=mid+1,y=t[y].r,t[ntot].r=ntot+1;
        ntot++;
        t[ntot]=t[y],t[ntot].sum+=v;
    }
    return tmp;
}
int tp;
LL ans=0;
void del(int pos,int v)
{
    for(int i=pos;i<=n;i+=lowbit(i))
        s[i]=build(1,n,s[i],v,-1); //樹狀數組上主席樹的修改,將其所能造成影響的時間軸的主席樹全部進行修改
}
int query(int l,int r,int x,int pos)
{
    if(pos<1)return 0;//特判,之前調試的時候加上的好像沒什麼卵用?
    for(int i=x;i;i-=lowbit(i))too[i]=s[i];//將所用到的樹狀數組上的修改狀態記下來;
    int nrt=root[x];
    int tans=0;
    while(l!=r)
    {
        int mid=(l+r)>>1;
        if(pos<=mid)
        {
            for(int i=x;i;i-=lowbit(i))too[i]=t[too[i]].l;
            nrt=t[nrt].l;
            r=mid;
        }
        else
        {
            for(int i=x;i;i-=lowbit(i))tans+=t[t[too[i]].l].sum;//因爲是求的前綴和,要把左區間所有的值加起來
            tans+=t[t[nrt].l].sum;
            for(int i=x;i;i-=lowbit(i))too[i]=t[too[i]].r;
            nrt=t[nrt].r;
            l=mid+1;
        }
    }
    for(int i=x;i;i-=lowbit(i))tans+=t[too[i]].sum;//這裏加的值是當前區間被修改的量
    tans+=t[nrt].sum;
    return tans;
}
int findv(int x,int nl,int nr)
{
    return query(1,n,x,nr)-query(1,n,x,nl-1);//左右端點的值相減
}
int read()
{
    char c=getchar();
    int tmp=0;
    while(!isdigit(c))c=getchar();
    while(isdigit(c))
    {
        tmp=tmp*10+c-'0';
        c=getchar();
    }
    return tmp;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        lc[a[i]]=i;
    }
    for(int i=1;i<=n;i++)
    {
        ans+=(LL)findv(i-1,a[i]+1,n); 
        root[i]=build(1,n,root[i-1],a[i],1);
    }
    printf("%lld\n",ans);
    for(int i=1;i<m;i++)
    {
        tp=read();
        if(tp<n)
            ans-=(LL)findv(lc[tp]-1,tp+1,n);//查詢小於這個數的位置中值大於這個數的數目和
        if(tp>1)
            ans-=(LL)findv(n,1,tp-1)-(LL)findv(lc[tp]-1,1,tp-1); //所求區間爲總區間的數目減去不符合的區間的數目
        del(lc[tp],tp);
        printf("%lld\n",ans);   
    }
    return 0;   
}

題目:bzoj 2653 middle
clj出的題果然就是神,整體構思非常巧妙,所以我身爲蒟蒻又是看的題解
對於每個數,大於等於這個數爲1,小於則爲-1
先將所有值排序,按值的大小爲主席樹的時間軸建樹,每次把這個位置改成-1;
如果一個數能夠成爲中位數,那麼給定區間中肯定有一個區間和爲0或1;
而在以這個數爲時間點,所有小於等於這個數的位置全被改成-1,所以應以當前時間前一個時間點的主席樹開始求值;
題目要求的是最大中位數,所以如果以mid爲值求的最大和大於等於0的話,說明真正的值大於等於mid,這樣就可以二分了;
這裏的mid是以數值排完序後數組下標
而最大和需要求中間覆蓋區間的區間和以及左邊的最大右區間和右邊的最大左區間,在線段樹上都可以很好的維護;

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#define LL long long
#define random(a,b) (a+rand()%(b-a+1))
const int maxn=20005;
int n,m;
using std::max;
struct asd
{
    int v,lc; 
    bool operator <(const asd & p)
    const {
    return v<p.v;
    }
}a[maxn];
struct qwe
{
    int l,r,sum,lmax,rmax;
}t[maxn*40]; 
int ntot=0;
int root[maxn];
void updata(int x)
{
    int lson=t[x].l;
    int rson=t[x].r;
    t[x].sum=t[lson].sum+t[rson].sum;
    t[x].lmax=max(t[lson].lmax,t[lson].sum+t[rson].lmax);
    t[x].rmax=max(t[rson].rmax,t[rson].sum+t[lson].rmax); 
} 
void build(int l,int r,int &x)
{
    x=++ntot;
    if(l==r)
    {
        t[x].sum=t[x].lmax=t[x].rmax=1;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,t[x].l);
    build(mid+1,r,t[x].r);
    updata(x);
}
void insert(int l,int r,int &x,int y,int pos,int v)
{
    t[++ntot]=t[y],x=ntot;
    if(l==r)
    {
        t[x].lmax=t[x].rmax=t[x].sum=v;
        return;     
    } 
    int mid=(l+r)>>1;
    if(pos<=mid)insert(l,mid,t[x].l,t[y].l,pos,v);
    else insert(mid+1,r,t[x].r,t[y].r,pos,v);
    updata(x); 
}
int querysum(int l,int r,int x,int nl,int nr)
{
    if(nl<=l&&nr>=r)return t[x].sum;
    int mid=(l+r)>>1;
    int tans=0;
    if(nl<=mid)tans+=querysum(l,mid,t[x].l,nl,nr);
    if(nr>mid)tans+=querysum(mid+1,r,t[x].r,nl,nr); 
    return tans;
}
int querylmax(int l,int r,int x,int nl,int nr)
{
    if(nl<=l&&nr>=r)return t[x].lmax;
    int mid=(l+r)>>1;
    if(nl<=mid&&nr>mid)
        return max(querysum(l,mid,t[x].l,nl,nr)+querylmax(mid+1,r,t[x].r,nl,nr),querylmax(l,mid,t[x].l,nl,nr)); 
    else if(nl<=mid)
        return querylmax(l,mid,t[x].l,nl,nr);
    else return querylmax(mid+1,r,t[x].r,nl,nr);
}
int queryrmax(int l,int r,int x,int nl,int nr)
{
    if(nl<=l&&nr>=r)return t[x].rmax;
    int mid=(l+r)>>1;
    if(nl<=mid&&nr>mid)
        return max(queryrmax(l,mid,t[x].l,nl,nr)+querysum(mid+1,r,t[x].r,nl,nr),queryrmax(mid+1,r,t[x].r,nl,nr));   
    else if(nl<=mid)
        return queryrmax(l,mid,t[x].l,nl,nr);
    else return queryrmax(mid+1,r,t[x].r,nl,nr);
}
int findans(int x,int u,int v,int y)
{
    int l=1,r=n;
    int tans=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        int sumv=querysum(1,n,root[mid-1],u,v);//求中間的區間和
        if(u>x)
        sumv+=max(queryrmax(1,n,root[mid-1],x,u-1),0);//這裏需要和0進行比較,因爲上邊的區間和是帶端點的
        if(v<y)
        sumv+=max(querylmax(1,n,root[mid-1],v+1,y),0);
        if(sumv>=0)l=mid+1,tans=mid;
        else r=mid-1;
    }
    return a[tans].v;
}
int tp[4],q;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i].v);
        a[i].lc=i;
    }
    build(1,n,root[0]);
    std::sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
        insert(1,n,root[i],root[i-1],a[i].lc,-1);//將這個數的位置上的值標爲-1;
    scanf("%d",&q);
    int la=0; 
    for(int i=1;i<=q;i++)
    {
        for(int j=0;j<4;j++)
        {
            scanf("%d",&tp[j]);
            tp[j]=(tp[j]+la)%n;
            tp[j]++;
        }
        std::sort(tp,tp+4);
        printf("%d\n",la=findans(tp[0],tp[1],tp[2],tp[3])); 
    }
    return 0;        
} 

題目:scoi2014 旅行
一道比較好想的樹鏈剖分加線段樹做法,而且只需要單點修改和區間求和以及求最值;
給每一種顏色建一顆線段樹,一開始並沒有任何節點,進行動態開點;
將所有節點其所對應顏色的線段樹上的對應位置加上這個點的權值,每一次修改時空都是logn
對於修改操作,如果改顏色,將當前顏色線段樹上的值設爲0,新顏色線段樹上的值加上這個節點的權值
如果改權值,直接改對應顏色線段樹上的值,不要忘了數組中的值也要改;
剩下的就是樹剖的常見操作了
我比較閒的是,線段樹不想寫遞歸版的,於是記了線段樹上每個點的父親,修改完之後再倒着updata一下,中間還手殘了調了很久。。。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#define LL long long
#define random(a,b) (a+rand()%(b-a+1))
int n,q;
using std::max;
using std::swap;
const int maxn=100005;
int tp1,tp2;
int node[maxn];
int siz[maxn],d[maxn],start[maxn],tp[maxn],fa[maxn],son[maxn];
int a[maxn],b[maxn],c[maxn];
int rt[maxn];
struct qwe
{
    int l,r,sum,mv,fa,lv,rv;
    qwe()
    {
        fa=0;
    }
}t[maxn*40];
struct asd
{
    int next,to;
}edge[maxn*2];
int etot=0;
void add(int x,int y)
{
    edge[++etot].next=node[x];
    node[x]=etot;   
    edge[etot].to=y;
}
int dfs(int x)
{
    siz[x]=1;
    son[x]=0;
    d[x]=d[fa[x]]+1;
    for(int i=node[x];i;i=edge[i].next)
    if(edge[i].to!=fa[x])
    {
        fa[edge[i].to]=x;
        siz[x]+=dfs(edge[i].to);
        if(siz[son[x]]<siz[edge[i].to])son[x]=edge[i].to;
    }   
    return siz[x];
}
int tot=0; 
void dfs1(int x,int top)
{
    tp[x]=top;
    start[x]=++tot;
    if(son[x])dfs1(son[x],top);
    for(int i=node[x];i;i=edge[i].next)
        if(edge[i].to!=fa[x]&&edge[i].to!=son[x])dfs1(edge[i].to,edge[i].to);
}
int ntot=0;
void modify(int x,int pos,int v)
{
    int l=1,r=n;
    while(l!=r)
    {
        t[x].lv=l,t[x].rv=r;
        int mid=(l+r)>>1;
        if(pos<=mid) {
            if(t[x].l)x=t[x].l;
            else t[x].l=++ntot,t[ntot].fa=x,x=ntot;
            r=mid;
        }   else {
            if(t[x].r)x=t[x].r;
            else t[x].r=++ntot,t[ntot].fa=x,x=ntot;
            l=mid+1;
            }
    }
    t[x].lv=t[x].rv=l;
    t[x].sum=t[x].mv=v;
    while(x=t[x].fa)
    {
        t[x].sum=t[t[x].l].sum+t[t[x].r].sum;
        t[x].mv=max(t[t[x].l].mv,t[t[x].r].mv);
    }
}
int query(int l,int r,int x,int nl,int nr,int check)
{
    if(nl<=l&&nr>=r)
    {
        if(check)return t[x].sum;
        else return t[x].mv;
    }
    int mid=(l+r)>>1;
    int ans=0;
    if(nl<=mid&&t[x].l)
    {
        if(check)ans+=query(l,mid,t[x].l,nl,nr,check);
        else ans=max(ans,query(l,mid,t[x].l,nl,nr,check));
    }
    if(nr>mid&&t[x].r)
    {
        if(check)ans+=query(mid+1,r,t[x].r,nl,nr,check);
        else ans=max(ans,query(mid+1,r,t[x].r,nl,nr,check));
    }
    return ans;
}
int findans(int x,int y,int check)
{
    int ans=0;
    int tc=c[x];
    int tx=tp[x];
    int ty=tp[y];
    while(tx!=ty)
    {
        if(d[tx]<d[ty]){
            swap(tx,ty);
            swap(x,y); 
        }
        if(check) 
        ans+=query(1,n,rt[tc],start[tx],start[x],check);
        else ans=max(ans,query(1,n,rt[tc],start[tx],start[x],check));
        x=fa[tx];
        tx=tp[x];   
    }
    if(d[x]<d[y])swap(x,y);
    {
        if(check) 
            ans+=query(1,n,rt[tc],start[y],start[x],check);
        else ans=max(ans,query(1,n,rt[tc],start[y],start[x],check));
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i],&c[i]);
    }
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&tp1,&tp2);
        add(tp1,tp2); 
        add(tp2,tp1);
    } 
    dfs(1);
    dfs1(1,1);
    for(int i=1;i<=100000;i++)rt[i]=i;
    ntot=100000;
    for(int i=1;i<=n;i++)
        modify(rt[c[i]],start[i],a[i]); 
    char s[5];
    while(q--)
    {
        scanf("%s%d%d",s,&tp1,&tp2);
        if(s[1]=='C')
        {
            modify(rt[c[tp1]],start[tp1],0);
            c[tp1]=tp2;
            modify(rt[c[tp1]],start[tp1],a[tp1]);
        }
        else if(s[1]=='W')
        {
            a[tp1]=tp2;
            modify(rt[c[tp1]],start[tp1],a[tp1]);
        }
        else if(s[1]=='S')
            printf("%d\n",findans(tp1,tp2,1));
        else
            printf("%d\n",findans(tp1,tp2,0));
    }
    return 0;
}
發佈了30 篇原創文章 · 獲贊 4 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章