加帕里的聚会 树链剖分 + 线段树(区间加 、区间赋值)

加帕里的聚会

256MB / 1s ; japari.cpp / c /pas / in / out

【题目描述】

加帕里公园里有n个区域,n-1条道路将它们连接到了一起,形成了一个树的结构。开始时,第i个区域有Ai个friends,但是由于砂之星的作用,有时从x区域到y区域的简单路径上的所有区域的friends数量都会增加v,有时从x区域到y区域的简单路径上所有区域的friends数量都会变成v。

有时,从x区域到y区域的简单路径上所有区域的friends想要聚会,聚会时需要从所有参加聚会的friends中选择一部分作为staff。加帕里的friends都很喜欢质数,因此她们希望staff和非staff的参与者的数量都是质数。

请你告诉她们,每次聚会是否存在一种方案满足她们的要求。

 

【输入格式】

第一行两个整数n,m,表示friends数量和事件数量。

接下来一行n个数,第i个数表示Ai。

接下来n-1行,每行2个数x,y,表示x和y之间有一条无向道路。

接下来m行,每行第一个整数opt表示事件类型。

若opt=1,接下来三个整数x,y,v,表示从x区域到y区域的简单路径上的所有区域的friends数量都增加v。

若opt=2,接下来三个整数x,y,v,表示从x区域到y区域的简单路径上的所有区域的friends数量都变为v。

若opt=3,接下来两个整数x,y,表示从x区域到y区域的简单路径上的所有区域的friends进行聚会。

 

【输出格式】

对于每个3事件,若可以让staff和非staff的数量都为质数,输出一行SUGOI,否则输出一行TANOSHI。

 

【样例数据】

japari1.in

japari1.out

3 4

1 2 1

2 1

3 2

2 2 1 4

1 3 3 -1

2 2 2 2

3 1 3

SUGOI

japari2.in

japari2.out

4 6

2 4 0 0

2 1

3 2

4 3

2 1 4 2

1 4 4 9

1 3 2 -2

2 4 2 5

3 1 4

3 4 4

TANOSHI

SUGOI

 

【样例解释】

在第一个样例中,三个区域的friends数量依次为4、2、0,询问的friends和为6,可以分成两组,每组的friends数量都为3。

在第二个样例中,四个区域的friends数量依次为2、5、5、5,第一个询问的friends和为17,无法分成两组。第二个询问的friends和为5,可以分为两组,每组的friends数量分别为2、3。

 

【数据范围】

对于30%的数据,n,m≤5000。

对于另30%的数据,对于i>1,第i个区域与第i-1个区域直接相连。

对于100%的数据,1≤n,m≤100000,1≤x,y≤n,一直满足0≤Ai,S≤10000000。在增加事件中v可能为负数。


一道树链剖分裸题, 然而我被坑的好惨...话说说好的NOIP模拟呢...哪有t1就考树链剖分加线段树的...这道题主要是区间赋值与区间加很烦人, 在pushdown的时候要考虑清楚逻辑关系.

讲一下怎么做. 树链剖分之后, 线段树维护dfs序, 两点之间的路径直接跳树链剖分剖出的链往上跳就可以, 边跳边修改线段树. 注意线段树中只能存在一个标记. 在修改的时候:

赋值修改 + 赋值标记 : 标记还是赋值标记, 将当前存的值改成修改的值.

赋值修改 + 增加标记:  标记改成赋值标记, 将当前存的值改成修改的值. (因为增加了再赋值相当于没赋值).

增加修改 + 赋值标记:  标记还是赋值标记, 将当前存的值增加修改的值. (因为赋值了之后再增加相当于在原来赋值的基础上赋一个本身加上增加的值)

增加修改 + 增加标记;  标记还是增加标记, 将当前存的值增加修改的值.

注意一下pushdown的时候如果当前是增加标记, 两个儿子如果是增加标记或者没有标记就都改成是增加标记, 如果是赋值标记的话就在两个儿子存的值上加上一个修改的值, 不要去改两个儿子的标记. 

#include<stdio.h>
#include<algorithm>
using namespace std;
const int lim  = 10005;
const int maxn = 200005;
bool mark[lim + 1]; int pr[lim];
int n, m, num, indexx, opt, tot;
int dep[maxn], in[maxn], top[maxn], siz[maxn], son[maxn], h[maxn], fa[maxn], seq[maxn], a[maxn];
inline const int read(){
	register int f = 1, x = 0;
	register char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return f * x;
}
inline bool judge(int x){
	if(x <= 3) return false;
	if(x&1){
		x -= 2;
		for(int i = 1; i <= tot && pr[i] * pr[i] <= x; ++i)
			if((x % pr[i]) == 0) return false;	
	}
	return true;
}
inline void Linear_sieve(){
	for(int i = 2; i <= lim; ++i){
		if(!mark[i]) pr[++tot] = i;
		for(int j = 1; j <= tot && pr[j] * i <= lim; ++j){
			mark[i * pr[j]] = true;
			if((i % pr[j]) == 0) break;
		}
	}
}
struct edge{ int nxt, v;}e[maxn * 2];
inline void add(int u, int v){
	e[++num].v = v, e[num].nxt = h[u], h[u] = num;
	e[++num].v = u, e[num].nxt = h[v], h[v] = num;
}
struct node{
	node *ls, *rs;
	int sum, flag, ad;
	void pushdown(int lf, int rg){
		if(flag == 1){
			int mid  = (lf + rg) >> 1;
			ls->sum += (mid - lf + 1) * ad;
			rs->sum += (rg - mid) * ad;
			if(!ls->flag) ls->flag = 1;
			if(!rs->flag) rs->flag = 1;
			ls->ad  +=ad , rs->ad += ad;  		
		}
		if(flag == 2){
			int mid  = (lf + rg) >> 1;
			ls->sum  = (mid - lf + 1) * ad;
			rs->sum  = (rg - mid) * ad;
			ls->flag = rs->flag = 2;
			ls->ad   = rs->ad   = ad;
		}
		flag = 0, ad = 0;
	}
	void update(){
		sum = ls->sum + rs->sum;
	}
}pool[maxn * 2], *tail = pool, *root;
node* build(int lf ,int rg){
	node *bt = ++tail;
	if(lf == rg) {bt->sum = a[seq[lf]]; bt->ad = bt->flag = 0; return bt;}
	int mid = (lf + rg)>>1;
	bt->ls = build(lf, mid), bt->rs = build(mid + 1, rg);
	bt->ad = 0, bt->flag = 0, bt->update();
	return bt;
}
void modify(node *bt, int lf, int rg, int L, int R, int delta, int type){
	if(L <= lf && rg <= R){
		if(type == 2) bt->flag = 2, bt->sum = (rg - lf + 1) * delta, bt->ad = delta;
		if(type == 1){
			bt->sum += (rg - lf + 1) * delta;
			if(bt->flag == 2)  bt->ad += delta;
			else bt->flag = 1, bt->ad += delta;
		}
		return;
	}
	int mid = (lf + rg)>>1;
	bt->pushdown(lf, rg);
	if(L <= mid) modify(bt->ls, lf, mid, L, R, delta, type);
	if(R > mid)  modify(bt->rs, mid + 1, rg , L, R, delta, type);
	bt->update();
	return;
}
int query(node *bt, int lf, int rg, int L, int R){
	if(L <= lf && rg <= R) return bt->sum;
	int mid = (lf + rg) >> 1, rt = 0;
	bt->pushdown(lf, rg);
	if(L <= mid)  rt += query(bt->ls, lf, mid, L, R);
	if(R > mid) rt += query(bt->rs, mid + 1, rg, L, R);
	bt->update();
	return rt;
}
void dfs1(int u){
	siz[u] = 1;
	for(int i = h[u]; i; i = e[i].nxt){
		int v = e[i].v;
		if(v == fa[u]) continue;
		fa[v] = u, dep[v] = dep[u] + 1;
		dfs1(v), siz[u] += siz[v];
		if(siz[v] > siz[son[u]]) son[u] = v;
	}
}
void dfs2(int u, int tp){
	top[u] = tp;
	in[u] = ++indexx; seq[indexx] = u;
	if(son[u]) dfs2(son[u], tp);
	for(int i = h[u]; i; i = e[i].nxt){
		int v = e[i].v;
		if(v == fa[u] || v == son[u]) continue;
		dfs2(v, v);
	}
}
void modify_tree(int u, int v, int delta, int type){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		modify(root, 1, n, in[top[u]], in[u], delta, type);
		u = fa[top[u]];
	}
	if(dep[u] < dep[v]) swap(u, v);
	modify(root, 1, n, in[v], in[u], delta, type);
}
int query_seg(int u, int v){
	int rt = 0;
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]]) swap(u, v);
		rt += query(root, 1, n, in[top[u]], in[u]);
		u = fa[top[u]];
	}
	if(dep[u] < dep[v]) swap(u, v);
	rt += query(root, 1, n, in[v], in[u]);
	return rt;
}
int main(){
	freopen("japari.in", "r", stdin);
	freopen("japari.out", "w", stdout);
	Linear_sieve();
	n = read(), m = read();
	for(register int i = 1; i <= n; ++i) a[i] = read();
	for(register int i = 1; i < n; ++i){
		int u = read(), v = read();
		add(u, v);
	}
	dfs1(1), dfs2(1, 1);
	root = build(1, n);
	for(register int i = 1; i <= m; ++i){
		opt = read();
		int x, y, v;
		if(opt == 1){
			x = read(), y = read(), v = read();
			modify_tree(x, y, v, 1);
		}
		if(opt == 2){
			x = read(), y = read(), v = read();
			modify_tree(x, y, v, 2);
		}
		if(opt == 3){
			x = read(), y = read();
			v = query_seg(x, y);
			if(judge(v)) puts("SUGOI");
			else puts("TANOSHI");
		}
	}
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值