1. 程式人生 > >Cogs 2856. [洛谷U14475]部落沖突

Cogs 2856. [洛谷U14475]部落沖突

文件 協議 splay size 允許 來源 || 二次 lin

2856. [洛谷U14475]部落沖突

★★★ 輸入文件:lct.in 輸出文件:lct.out 簡單對比
時間限制:1 s 內存限制:256 MB

【題目描述】

在一個叫做Travian的世界裏,生活著各個大大小小的部落。其中最為強大的是羅馬、高盧和日耳曼。他們之間為了爭奪資源和土地,進行了無數次的戰鬥。期間誕生了眾多家喻戶曉的英雄人物,也留下了許多可歌可泣的動人故事。

其中,在大大小小的部落之間,會有一些道路相連,這些道路是Travian世界裏的重要樞紐,簡單起見,你可以把這些部落與部落之間相連的道路看作一顆樹,可見每條道路對於Travian世界的重要程度。有了這些道路,建築工人就可以通過這些道路進行友好外交啦。

然而,事情並不會像想象的那樣美好,由於資源的匱乏,相鄰的部落(由一條道路相連的部落)之間經常會發生大大小小的沖突事件,更有甚者,會升級為部落之間的大型戰爭。

為了避免誤傷,每當兩個相鄰的部落之間發生大型戰爭之時,這兩個部落間的道路是不允許通行的,對於一些強大的部落,甚至能與多個相鄰的部落同時開戰,同樣的,這些戰爭地帶的道路十分危險,是不可通行的。

天下之勢,分久必合,當兩個部落經歷了不打不相識的苦戰之後,他們可以簽訂停戰協議(暫時停戰,以後依舊可能再次開戰),這樣,兩個部落之間的道路又會重新恢復為可通行狀態,建築工人們又可以經過此地購買最新的大本營設計圖紙來強大自己的部落了。

為了簡單起見,我們把各大戰爭事件按發起的時間順序依次編號(最先發起的戰爭編號就為 1,第二次戰爭編號就為 2,以此類推),當兩個部落停戰之時,則會直接告訴你這場戰爭的編號,然後這場戰爭就載入了史冊,不復存在了,當然,這並不會影響到其他戰爭的編號。

建築工人十分討厭戰爭,因為戰爭,想從一個部落到另一個部落進行友好交流的建築工人可能就此白跑一趟。所以,在他們出發之前,都會向你問問能不能到達他們想去的部落。

技術分享

【輸入格式】

第一行兩個數 n 和 m, n 代表了一共有 n 個部落,m 代表了以上三種事件發生的總數

接下來的 n - 1行,每行兩個數 p , q,代表了第 p 個部落與第 q個部落之間有一條道路相連

接下來的 m 行,每行表示一件事,詳見題目描述

【輸出格式】

每行一個“Yes”或者“No”,表示從第 p個部落出發的建築工人能否到達第 q個部落

【樣例輸入】

5 9

1 2

2 3

3 4

4 5

Q 1 4

C 2 1

C 4 3

Q 3 1

Q 1 5

U 1

U 2

C 4 3

Q 3 4

【樣例輸出】

Yes

No

No

No

【提示】

對於30%的數據 1<=n,m<=6000

對於另30%的數據,保證部落之間的地理關系是一條鏈,且 i 與 i + 1 之間有一條道路

對於另30%的數據,1<=n,m<=100000

對於100%的數據,1<=n,m<=300000

【來源】

NOIP 模擬賽 by WISCO信息組

技術分享
#include<iostream>
#include<cstdio>
#define maxn 300010
using namespace std;
int n,m,num,head[maxn],s[maxn],t[maxn],cnt,fa[maxn],dep[maxn],edge[maxn];
int son[maxn],top[maxn],sz[maxn];
struct node{
    int to,pre,v;
}e[maxn*2];
void Insert(int from,int to){
    e[++num].to=to;
    e[num].pre=head[from];
    head[from]=num;
}
char ch[5];
void dfs(int now,int father){
    fa[now]=father;
    dep[now]=dep[father]+1;
    sz[now]=1;
    for(int i=head[now];i;i=e[i].pre){
        int to=e[i].to;
        if(to==father)continue;
        dfs(to,now);
        sz[now]+=sz[to];
        edge[to]=i;
        if(!son[now]||sz[son[now]]<sz[to])son[now]=to;
    }
}
void dfs2(int now,int father){
    top[now]=father;
    if(son[now])dfs2(son[now],father);
    for(int i=head[now];i;i=e[i].pre){
        int to=e[i].to;
        if(to==son[now]||to==fa[now])continue;
        dfs2(to,to);
    }
}
int qlca(int a,int b){
    while(top[a]!=top[b]){
        if(dep[top[a]]<dep[top[b]])swap(a,b);
        a=fa[top[a]];
    }
    if(dep[a]>dep[b])swap(a,b);
    return a;
}
void change(int a,int b,int v){
    int lca=qlca(a,b);
    while(1){
        if(a==lca)break;
        e[edge[a]].v+=v;
        a=fa[a];
    }
    while(1){
        if(b==lca)break;
        e[edge[b]].v+=v;
        b=fa[b];
    }
}
int query(int a,int b){
    int res=0;
    int lca=qlca(a,b);
    while(1){
        if(a==lca)break;
        res=max(res,e[edge[a]].v);
        a=fa[a];
    }
    while(1){
        if(b==lca)break;
        res=max(res,e[edge[b]].v);
        b=fa[b];
    }
    return res;
}
int main(){
//    freopen("Cola.txt","r",stdin);
    freopen("lct.in","r",stdin);freopen("lct.out","w",stdout);

    scanf("%d%d",&n,&m);
    int x,y,z;
    for(int i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        Insert(x,y);Insert(y,x);
    }
    dfs(1,0);
    dfs2(1,1);
    for(int i=1;i<=m;i++){
        scanf("%s",ch);
        if(ch[0]==Q){
            scanf("%d%d",&x,&y);
            int flag=query(x,y);
            if(flag)puts("No");
            else puts("Yes");
        }
        if(ch[0]==C){
            scanf("%d%d",&x,&y);
            cnt++;s[cnt]=x;t[cnt]=y;
            change(x,y,1);
        }
        if(ch[0]==U){
            scanf("%d",&z);
            x=s[z];y=t[z];
            change(x,y,-1);
        }
    }
    return 0;
}
95分 暴力 技術分享
/*
    樹剖維護樹上的兩種操作:
    1.修改兩點之間所有邊的邊權
    2.詢問兩點之間邊權總和是否為0 
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c<0||c>9){if(c==-)f=-1;c=getchar();}
    while(c>=0&&c<=9){x=x*10+c-0;c=getchar();}
    return x*f;
}
const int maxn=300010;
int n,m;
struct ww{int u,v;}war[maxn];
int wartot;
vector<int>t[maxn];
int tot,sz[maxn],son[maxn],fa[maxn],dep[maxn],top[maxn],id[maxn],pre[maxn];
inline void dfs1(int u,int f,int d){
    sz[u]=1;dep[u]=d;fa[u]=f;
    int len=t[u].size();
    for(int i=0;i<len;i++){
        int v=t[u][i];
        if(v==f)continue;
        dfs1(v,u,d+1);
        sz[u]+=sz[v];
        if(!son[u]||sz[son[u]]<sz[v])son[u]=v;
    }
}
inline void dfs2(int u,int tou){
    top[u]=tou;id[u]=++tot;pre[tot]=u;
    if(!son[u])return;
    dfs2(son[u],tou);
    int len=t[u].size();
    for(int i=0;i<len;i++){
        int v=t[u][i];
        if(v!=fa[u]&&v!=son[u])dfs2(v,v);
    }
}
int sum[maxn<<2];
inline void change(int o,int l,int r,int nl,int nr,int v){
    if(l>=nl&&r<=nr){
        sum[o]+=v;
        return;
    }
    int m=(l+r)>>1,ls=o<<1,rs=ls|1;
    if(m>=nl)change(ls,l,m,nl,nr,v);
    if(m<nr)change(rs,m+1,r,nl,nr,v);
    sum[o]=sum[ls]+sum[rs];
}
inline int find(int o,int l,int r,int nl,int nr){
    if(l>=nl&&r<=nr)return sum[o];
    int m=(l+r)>>1,ls=o<<1,rs=ls|1;
    int ans=0;
    if(m>=nl)ans+=find(ls,l,m,nl,nr);
    if(m<nr)ans+=find(rs,m+1,r,nl,nr);
    return ans;
}
inline void ok(int x,int y){
    int u=top[x],v=top[y];
    int ans=0;
    while(u!=v){
        if(dep[u]<dep[v])swap(u,v),swap(x,y);
        ans+=find(1,1,tot,id[u],id[x]);
        x=fa[u];u=top[x];
    }
    if(dep[x]>dep[y])swap(x,y);
    if(x!=y)//以邊樹剖時加特判。 
        ans+=find(1,1,tot,id[son[x]],id[y]);
    if(ans)puts("No");
    else puts("Yes");
}
int main(){
    freopen("lct.in","r",stdin);
    freopen("lct.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<n;i++){
        int u=read(),v=read();
        t[u].push_back(v);
        t[v].push_back(u); 
    }
    dfs1(1,0,1);
    dfs2(1,1);
    for(int i=1;i<=m;i++){
        char c=getchar();
        while(c!=Q&&c!=C&&c!=U)c=getchar();
        if(c==Q){
            int x=read(),y=read();
            ok(x,y);
        }
        if(c==C){
            int ru=1;
            war[++wartot].u=read(),war[wartot].v=read();
            int to;
            if(war[wartot].u>war[wartot].v)to=war[wartot].u;
            else to=war[wartot].v;
            change(1,1,tot,id[to],id[to],ru);
        }
        if(c==U){
            int ru=-1;
            int x=read();
            int to;
            if(war[x].u>war[x].v)to=war[x].u;
            else to=war[x].v;
            change(1,1,tot,id[to],id[to],ru);
        }
    }
    return 0;
}
100分 樹剖

Cogs 2856. [洛谷U14475]部落沖突