1. 程式人生 > >【例題】【樹鏈剖分】

【例題】【樹鏈剖分】

1、
【USACO 2011 Dec Gold 】種草
時間限制 : 10000 MS 空間限制 : 65536 KB

問題描述
農夫約翰有N塊貧瘠的牧場(2 <= N <= 100,000),有N-1條雙向道路將這N個牧場連線了起來,每兩個牧場間都有且僅有一條路徑可相互到達。著名奶牛貝西經常抱怨:為什麼連線牧場的道路上沒有草可吃呢?
約翰非常喜歡貝西,今天約翰終於決定要在道路上種草了。約翰的種草工作被分成了M(1 <= M <=100,000)步操作。
在每一步中,下列兩個事件中的一個會發生:
1.約翰會選擇兩個牧場,沿著兩個牧場間的路徑,在路徑上的每一條道路上都種植1株牧草;
2.貝西會向約翰提問:在一條指定的道路上,種植了多少株牧草;
請幫助約翰回答貝西的問題。

輸入格式
第一行,兩個空格間隔的整數N和M
接下來N-1行,每行兩個整數x和y,表示牧場x和y之間有道路直接相連
接下來M行,每行描述一步操作:
每行以字母P或Q作為開頭,P代表種草操作,Q代表詢問操作,接下來兩個整數,A_i 和 B_i用於描述該步的操作(1 <= A_i, B_i <= N)。

輸出格式
對於每一次詢問,輸出一行,一個整數,表示詢問的答案

樣例輸入
4 6
1 4
2 4
3 4
P 2 3
P 1 3
Q 3 4
P 1 4
Q 2 4
Q 1 4

樣例輸出
2
1
2
思路:
樹剖步驟:
1、深搜:找重鏈、找爸爸
2、再深搜:標鏈祖先、重標id
3、維護加亂搞:拆成多段…

注意:該題將邊權值儲存在點上,所以每次操作中最後在主鏈上的操作需要將x++,否則會多算一條,但是可能x++後出現x>y的情況,需特判

#include<cstdio>
#include<iostream>
using namespace std;
const int need=100004;

//............................................
inline void inc(char &c)
{
    c=getchar();
    while(c==' '||c==10) c=getchar();
} 
inline
void in_(int &d) { char t=getchar(); while(t<'0'||t>'9') t=getchar(); for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0'; } inline void out_(int x) { if(x>=10) out_(x/10); putchar(x%10+'0'); } //............................................ int tot; int fi[need],la[need<<1],en[need<<1]; inline void add() { int a,b;in_(a),in_(b); tot++; la[tot]=fi[a],en[tot]=b,fi[a]=tot; tot++; la[tot]=fi[b],en[tot]=a,fi[b]=tot; } //............................................ #define ls (s<<1) #define rs ((s<<1)|1) int val[need<<3],aa[need<<3],bb[need<<3],lazy[need<<3]; void build(int s,int l,int r) { aa[s]=l,bb[s]=r; if(l==r) return ; build(ls,l,(l+r)>>1),build(rs,(l+r)/2+1,r); } inline void putdown(int s) { int k=lazy[s]; lazy[s]=0; if(aa[s]==bb[s]) return ; val[ls]+=k,val[rs]+=k; lazy[ls]+=k,lazy[rs]+=k; } int x,y; void ins_(int s) { if(aa[s]>y||bb[s]<x) return ; if(x<=aa[s]&&bb[s]<=y) { val[s]+=bb[s]-aa[s]+1; lazy[s]++; return ; } if(lazy[s]) putdown(s); ins_(ls),ins_(rs); val[s]=val[ls]+val[rs]; } int ask_(int s) { if(aa[s]>y||bb[s]<x) return 0; if(x<=aa[s]&&bb[s]<=y) return val[s]; if(lazy[s]) putdown(s); return ask_(ls)+ask_(rs); } //............................................ int wws[need],fa[need],size[need],dep[need]; int idid,id[need],ancestor[need]; void find_w(int x) { int t,y,msize=0; size[x]=1; for(t=fi[x];t;t=la[t]) { y=en[t]; if(fa[x]==y) continue; fa[y]=x; dep[y]=dep[x]+1; find_w(y); size[x]+=size[y]; if(size[y]>msize) msize=size[y],wws[x]=y; } } void find_a(int x) { id[x]=++idid; if(wws[x]) find_a((ancestor[wws[x]]=ancestor[x],wws[x])); for(int t=fi[x],y;t;t=la[t]) { y=en[t]; if(y==fa[x]||y==wws[x]) continue; find_a(ancestor[y]=y); } } void tree(){find_w(1),find_a(ancestor[1]=1);} //............................................ void change(int u,int v) { while(ancestor[u]!=ancestor[v]) { if(dep[ancestor[u]]<dep[ancestor[v]]) swap(u,v);//深度大的往上 x=id[ancestor[u]],y=id[u]; ins_(1); u=fa[ancestor[u]]; } if(dep[u]>dep[v]) swap(u,v);//深度大的在後 x=id[u]+1,y=id[v]; if(x<=y) ins_(1); } int ask(int u,int v) { int ans=0; while(ancestor[u]!=ancestor[v]) { if(dep[ancestor[u]]<dep[ancestor[v]]) swap(u,v); x=id[ancestor[u]],y=id[u]; ans+=ask_(1); u=fa[ancestor[u]]; } if(dep[u]>dep[v]) swap(u,v); x=id[u]+1,y=id[v]; if(x<=y) ans+=ask_(1); return ans; } //............................................ int main() { int n,m;in_(n),in_(m); for(int i=1;i<n;i++) add(); tree(); build(1,1,n); char c; for(int i=1,a,b;i<=m;i++) { inc(c),in_(a),in_(b); if(c=='P') change(a,b); else out_(ask(a,b)),putchar(10); } }

2、
NKOJ2145【SDOI2011 第1輪 DAY1】染色
時間限制 : 40000 MS 空間限制 : 565536 KB

問題描述
給定一棵有個節點的無根樹和m個操作,操作有2類:
1、將節點a到節點b路徑上所有點都染成顏色c;
2、詢問節點a到節點b路徑上的顏色段數量(連續相同顏色被認為是同一段),如“112221”由3段組成:“11”、“222”和“1”。
請你寫一個程式依次完成這m個操作。

輸入格式
第一行包含2個整數n和m,分別表示節點數和運算元;
第二行包含n個正整數表示n個節點的初始顏色
下面n-1行每行包含兩個整數x和y,表示x和y之間有一條無向邊。
下面m行每行描述一個操作:
“C a b c”表示這是一個染色操作,把節點a到節點b路徑上所有點(包括a和b)都染成顏色;
“Q a b”表示這是一個詢問操作,詢問節點a到節點b(包括a和b)路徑上的顏色段數量。

輸出格式
對於每個詢問操作,輸出一行答案。

樣例輸入
6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5

樣例輸出
3
1
2

思路:
維護區間內段數、左端顏色、右端顏色,每次詢問若相鄰兩段相鄰顏色相同ans–

#include<cstdio>
#include<iostream>
using namespace std;
const int need=100004;

int n,m;
int a[need],aaa[need];
//............................................
inline void inc(char &c)
{
    c=getchar();
    while(c==' '||c==10) c=getchar();
} 
inline void in_(int &d)
{
    char t=getchar();
    while(t<'0'||t>'9') t=getchar();
    for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';
}
inline void out_(int x)
{
    if(x>=10) out_(x/10);
    putchar(x%10+'0');
}
//............................................
int tot,fi[need],la[need<<1],en[need<<1];
inline void add()
{
    int a,b;in_(a),in_(b);
    tot++;
    la[tot]=fi[a],fi[a]=tot,en[tot]=b;
    tot++;
    la[tot]=fi[b],fi[b]=tot,en[tot]=a;
}
//............................................
int fa[need],ww[need],si[need],dep[need];
int idid,id[need],anc[need];

void find_w(int x)
{
    int t,y,msi=0;
    si[x]=1;
    for(t=fi[x];t;t=la[t])
    {
        y=en[t];
        if(fa[x]==y) continue;
        fa[y]=x;
        dep[y]=dep[x]+1;
        find_w(y);
        si[x]+=si[y];
        if(si[y]>msi) {msi=si[y];ww[x]=y;}
    }
}
void find_a(int x)
{
    id[x]=++idid; 
    if(ww[x]) find_a((anc[ww[x]]=anc[x],ww[x]));
    for(int t=fi[x],y;t;t=la[t])
    {
        y=en[t];
        if(y==fa[x]||y==ww[x]) continue;
        find_a(anc[y]=y);
    }
}
void tree(){find_w(1),find_a(anc[1]=1);}
//............................................
#define ls (s<<1)
#define rs ((s<<1)|1)
int aa[need<<3],bb[need<<3],as[need<<3],bs[need<<3],tt[need<<3],lazy[need<<3];

inline void NBHB(int s)
{
    if(aa[s]==bb[s]) return ;
    as[s]=as[ls],bs[s]=bs[rs];
    tt[s]=tt[ls]+tt[rs];
    if(bs[ls]==as[rs]) tt[s]--;
}

void build(int s,int l,int r)
{
    aa[s]=l,bb[s]=r;
    if(l==r) 
    {
        tt[s]=1;
        as[s]=bs[s]=aaa[l];
        return ;
    }
    build(ls,l,(l+r)>>1),build(rs,(l+r)/2+1,r);
    NBHB(s);
}

inline void putdown(int s)
{
    int k=lazy[s];
    lazy[s]=0;
    if(aa[s]==bb[s]) return;
    lazy[ls]=lazy[rs]=k;
    as[ls]=bs[ls]=as[rs]=bs[rs]=k,tt[ls]=tt[rs]=1;
}

int x,y,d;

int getc(int s)
{
    if(aa[s]==bb[s]) return as[s];
    if(lazy[s]) return lazy[s];//該區間內所有點顏色都為lazy 
    int mid=(aa[s]+bb[s])/2;
    if(d<=mid) return getc(ls);
    else return getc(rs);
}

void change_(int s)
{
    if(aa[s]>y||bb[s]<x) return ;
    if(x<=aa[s]&&bb[s]<=y)
    {
        tt[s]=1;
        as[s]=bs[s]=d;
        if(aa[s]!=bb[s]) lazy[s]=d;
        return ;
    }
    if(lazy[s]!=0) putdown(s);
    change_(ls),change_(rs);
    NBHB(s);
}
int ask_(int s)
{
    if(aa[s]>y||bb[s]<x) return 0;
    if(x<=aa[s]&&bb[s]<=y) return tt[s];
    if(lazy[s]!=0) putdown(s);
    int mid=(aa[s]+bb[s])>>1;
    if(y<=mid) return ask_(ls);
    if(x>mid) return ask_(rs);
    return ask_(ls)+ask_(rs)-(bs[ls]==as[rs]);
}

//............................................
void change(int u,int v)
{
    while(anc[u]!=anc[v])
    {
        if(dep[anc[v]]>dep[anc[u]]) swap(u,v);
        x=id[anc[u]],y=id[u];
        change_(1);
        u=fa[anc[u]];
    }
    if(dep[v]>dep[u]) swap(u,v);
    x=id[v],y=id[u];
    change_(1);
}
int ask(int u,int v)
{
    int ans=0;
    while(anc[v]!=anc[u])
    {
        if(dep[anc[v]]>dep[anc[u]]) swap(u,v);
        x=id[anc[u]],y=id[u];
        ans+=ask_(1);
        int c,cf=0;
        if(fa[anc[u]]) d=id[fa[anc[u]]],cf=getc(1);
        d=id[anc[u]],c=getc(1);
        if(c==cf) ans--;
        u=fa[anc[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    x=id[u],y=id[v];
    ans+=ask_(1);
    return ans; 
}
//............................................

int main_()
{
     int n,m;in_(n),in_(m);
     for(int i=1;i<=n;i++) in_(a[i]);
     for(int i=1;i<n;i++) add();
     tree();
     for(int i=1;i<=n;i++) aaa[id[i]]=a[i];
     build(1,1,n);
     char b;
     for(int i=1,c,e;i<=m;i++) 
     {
        inc(b);
        if(b!='Q')
        {
            in_(c),in_(e),in_(d);
            change(c,e);
        }
        else 
        {
            in_(c),in_(e);
            out_(ask(c,e)),putchar(10);
        }
     } 
}

const int main_stack=16;
char my_stack[128<<20];

int main(){
     __asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");
     __asm__("movl %%eax, %%esp;\n"::"a"(my_stack+sizeof(my_stack)-main_stack):"%esp");
     main_();
     __asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");
     return 0;
}