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

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分 树剖

时间: 2024-11-09 03:11:27

Cogs 2856. [洛谷U14475]部落冲突的相关文章

洛谷 P2827 BZOJ 4721 UOJ #264 蚯蚓

题目描述 本题中,我们将用符号表示对c向下取整,例如:. 蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀手来帮他们消灭蚯蚓. 蛐蛐国里现在共有n只蚯蚓(n为正整数).每只蚯蚓拥有长度,我们设第i只蚯蚓的长度为,并保证所有的长度都是非负整数(即:可能存在长度为0的蚯蚓). 每一秒,神刀手会在所有的蚯蚓中,准确地找到最长的那一只(如有多个则任选一个)将其切成两半.神刀手切开蚯蚓的位置由常数p(是满足0<p<1的有理数)决定,设这只蚯蚓长度为x,神刀手会将其切成两只长度

洛谷P1083 借教室 二分 + 差分

洛谷P1083 借教室 二分 + 差分(或说前缀和,其实前缀和更准确一点) 首先二分答案,即取 mid 个人,且他们不会冲突 然后O(n) 判断是否冲突 如何判断呢,首先我们发现 一个人的操作相当于是将 一些连续的山削去了一个高度 然后我们可以记录这座山被消了多少高度,但这样一次就要 O(N) 总共(n^2) 但是我们发现高度差只有两个地方变了,一个是起始,一个是终止 t[ i ] 表示 h[ i ] - h[ i-1 ] 改变过后 于是 t[ s ]-=d,t[ t+1 ]+=d ; 然后这样

洛谷 P3178 BZOJ 4034 [HAOI2015]树上操作

题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a .操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a .操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 输入输出格式 输入格式: 第一行包含两个整数 N, M .表示点数和操作数.接下来一行 N 个整数,表示树中节点的初始权值.接下来 N-1 行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) .再接下来 M 行

洛谷P2024 食物链

挺神奇 题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的一种,但是我们并不知道 它到底是哪一种. 有人用两种说法对这 N 个动物所构成的食物链关系进行描述: 第一种说法是“1 X Y”,表示 X 和 Y 是同类. 第二种说法是“2 X Y”,表示 X 吃 Y . 此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真 的,有的是

洛谷 P1972 [SDOI2009]HH的项链

P1972 [SDOI2009]HH的项链 题目背景 无 题目描述 HH 有一串由各种漂亮的贝壳组成的项链.HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义.HH 不断地收集新的贝壳,因此,他的项链变得越来越长.有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了.于是,他只好求助睿智的你,来解决这个问题. 输入输出格式 输入格式: 第一行:一个整数N,表示项链的长度. 第二行:N 个整数,表示

洛谷P1525 关押罪犯 并查集

无冲突 输出 0 洛谷P1525 关押罪犯 并查集 用拆点法 将一个点拆成两份 一个点和 x 的朋友相连 一个点和 x的敌人相连 若 x 与 y 是敌人 因为只有两个阵营 所以满足敌人的敌人就是朋友 然后 x 连向 y 的敌人 y 连向 x 的敌人 因为这是双向边 所以 y的朋友就是x的敌人就不用连了 如果某一时刻 getfather(e[ i ].from)==getfather(e[ i ].to) 这说明两个点已经是敌人关系的两个人 通过朋友关系连上了 那就结束了 1 #include <

洛谷P1462 通往奥格瑞玛的道路

P1462 通往奥格瑞玛的道路 219通过 1.2K提交 题目提供者gconeice 标签二分图论洛谷原创 难度提高+/省选- 提交该题 讨论 题解 记录 最新讨论 RE好多.. 到底怎么判断AFK啊 看不懂题目.. 建议修改题目 究竟是血量为负算挂还是生命… 题目背景 在艾泽拉斯大陆上有一位名叫歪嘴哦的神奇术士,他是部落的中坚力量 有一天他醒来后发现自己居然到了联盟的主城暴风城 在被众多联盟的士兵攻击后,他决定逃回自己的家乡奥格瑞玛 题目描述 在艾泽拉斯,有n个城市.编号为1,2,3,...,

洛谷P1456Monkey King

洛谷P1456 Monkey King 题目描述 Once in a forest, there lived N aggressive monkeys. At the beginning, they each does things in its own way and none of them knows each other. But monkeys can't avoid quarrelling, and it only happens between two monkeys who do

洛谷.2219.[HAOI2007]修筑绿化带(单调队列)

题目链接 洛谷 COGS.24 对于大的矩阵可以枚举:对于小的矩阵,需要在满足条件的区域求一个矩形和的最小值 预处理S2[i][j]表示以(i,j)为右下角的C\(*\)D的矩阵和, 然后对于求矩形区域的最小值,可以先将每行看做一个数列,对于每个点y,得到一个[y-(B-3),y]的最小值 处理完行后得到Minr[][],对每列的进行同样的操作,就可以得到Min[x][y]表示([x-A+3,x],[y-B+3,y])的最小矩形和 但是注意单调队列处理的是S2,S2表示的是C\(*\)D的和,n