Codeforces Round #316 (Div. 2) D. Tree Requests(DFS+状态压缩)

题意:给定一棵树,n个节点。每一个节点处有一个字母,结点的深度定义为节点到根结点1的距离,

有m个询问(u。v),每次回答以结点u为根的子树的深度为v的那些节点处的字母是否能组成一个回文串,特别的,空串也是回文串。

思路:首先说明推断回文串的方法,仅仅要出现次数为奇数个字母个数不超过2。那么这些字母一定能够组成回文串。

接下来考虑将树转成线性结构。

利用dfs+时间戳将结点依照深度存入一个线性结构里,Depth[i]数组里存的是深度为i的全部结点,

那么对于询问有三种情况。一种是dep[u]>=v输出Yes

另外一种是desdep[u]<v(以结点u为根的子树中的最大节点深度),输出Yes

剩余情况为第三种,比較复杂也是我们要解决的问题

由于以结点u为根的深度为v的结点在Dep[v]数组中下标肯定是连续的,

所以利用时间戳两次二分找到以结点u为根的深度为v的结点在Dep[v]数组中的起始下标ql和截止下标qr,

由于我们仅仅须要推断每一个字母是否出现奇数次,所以我们能够用一个长26位的状态表示每一个字母出现奇数次还是偶数次,

并且这种话这个状态具有一个非常优美的性质,能够通过异或来转移状态

那么这样我们仅仅须要预处理出每一个深度的前缀的状态su[dep][i]就可以,

推断时仅仅需推断su[dep][qr] xor su[dep][ql-1]中有多少位为1就可以,

若一的位数小于2输出Yes,否则输出No。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii (pair<int, int>)
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int maxn = 500000+1000;
//const int INF = 0x3f3f3f3f;
vector<int> G[maxn];
vector<int> Depth[maxn], su[maxn];
int w[maxn];
int dep[maxn], tim[maxn][2], desdep[maxn];
int n, m, clock_cnt;
char str[maxn];

void dfs(int cur) {
    desdep[cur] = dep[cur];
	tim[cur][0] = ++clock_cnt;
	for(int i = 0; i < G[cur].size(); i++) {
		int u = G[cur][i];
		dep[u] = dep[cur] + 1;
		Depth[dep[u]].push_back(u);
		dfs(u);
		desdep[cur] = max(desdep[cur], desdep[u]);
	}
	tim[cur][1] = ++clock_cnt;
}

void init() {
	int d = 1;
	while(1) {
		int sz = Depth[d].size();
		if(!sz) break;
		for(int i = 0; i < sz; i++) {
			int t = w[Depth[d][i]];
			int tmp = su[d][i];
			su[d].push_back(tmp^(1<<t));
		}
		d++;
	}
}

bool check(int dep, int ql, int qr) {
	int palin = su[dep][qr]^su[dep][ql];
	int cnt = 0;
	for(int i = 0; i < 26; i++) {
		if((1<<i)&palin) cnt++;
	}
	if(cnt>1) return false;
	return true;
}

int main() {
    //freopen("input.txt", "r", stdin);
	while(scanf("%d%d", &n, &m) == 2) {
		for(int i = 1; i <= n; i++) G[i].clear(), Depth[i].clear(), su[i].clear();
		for(int i = 1; i <= n; i++) su[i].push_back(0);
		clock_cnt = 0;
		for(int i = 2; i <= n; i++) {
			int t; scanf("%d", &t);
			G[t].push_back(i);
		}
		Depth[1].push_back(1), dep[1] = 1;
		scanf("%s", str);
		for(int i = 1; i <= n; i++) w[i] = str[i-1]-'a';
		dfs(1);
		init();
		//for(int i = 1; i <= n; i++) cout << tim[i][0] << " " << tim[i][1] << endl;
		for(int i = 0; i < m; i++) {
			int u, v; scanf("%d%d", &u, &v);
			if(dep[u] >= v) printf("Yes\n");
			else if(desdep[u] < v) printf("Yes\n");
			else {
				int ql, qr;
				int L = 0, R = Depth[v].size()-1;
				while(L < R) {
					int M = (L+R)>>1;
					int tmp = Depth[v][M];
					if(tim[tmp][0] > tim[u][0]) R = M;
					else L = M+1;
				}
				ql = R;
				L = 0, R = Depth[v].size()-1;
				while(L <= R) {
					int M = (L+R)>>1;
					int tmp = Depth[v][M];
					if(tim[tmp][1] > tim[u][1]) R = M-1;
					else L = M+1;
				}
				qr = R;
				if(check(v, ql, qr+1)) printf("Yes\n");
				else printf("No\n");
			}
		}
	}
    return 0;
}



时间: 2024-07-30 16:42:32

Codeforces Round #316 (Div. 2) D. Tree Requests(DFS+状态压缩)的相关文章

Codeforces Round #316 (Div. 2) D. Tree Requests 树 离线在线 算法

D. Tree Requests time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Roman planted a tree consisting of n vertices. Each vertex contains a lowercase English letter. Vertex 1 is the root of the

Codeforces Round #316 (Div. 2) D Tree Requests

官方题解是离线询问,dfs树形转线性,然后二分找区间. 还有一种比较好的做法是直接dfs,将当前访问这个结点u相关的询问之前的状态存起来,然后访问完以后利用异或开关性,得到这颗子树上的答案. 代码是学习别人的http://blog.csdn.net/squee_spoon/article/details/47666667 当时做的时候想得是树形转线性,觉得dfs会暴栈,想用bfs,之前又没写过,于是愣了一个钟头. #include<bits/stdc++.h> using namespace

Codeforces Round #316 (Div. 2)

A - Elections 1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,num[105],a[105][105]; 4 int main() 5 { 6 scanf("%d%d",&n,&m); 7 for(int i=1;i<=m;i++) 8 { 9 int item=1; 10 for(int j=1;j<=n;j++) 11 { 12 scanf("%d&

Codeforces Round #540 (Div. 3) F1. Tree Cutting (Easy Version) 【DFS】

任意门:http://codeforces.com/contest/1118/problem/F1 F1. Tree Cutting (Easy Version) time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output You are given an undirected tree of nn vertices. Some vert

Codeforces Round #629 (Div. 3) E. Tree Queries(lca题)

https://codeforces.com/contest/1328/problem/E E. Tree Queries You are given a rooted tree consisting of nn vertices numbered from 11 to nn. The root of the tree is a vertex number 11. A tree is a connected undirected graph with n−1n−1 edges. You are

Codeforces Round #353 (Div. 2) D. Tree Construction (二分,stl_set)

题目链接:http://codeforces.com/problemset/problem/675/D 给你一个如题的二叉树,让你求出每个节点的父节点是多少. 用set来存储每个数,遍历到a[i]的时候查找比a[i]大的数的位置,然后插入,而父亲就是刚好比a[i]小的数或刚好大的数. 然后讨论是哪一个数. 比如给你3 1 2 ,如图 1的父亲是3 ,2的父亲是1. 那我其实只要找左边或右边出现最晚的数就行了,用pair的first表示a[i],second表示出现的顺序i. 1 #include

数据结构 - Codeforces Round #353 (Div. 2) D. Tree Construction

Tree Construction Problem's Link ---------------------------------------------------------------------------- Mean: 给定n个数,按照构造Binary Search Tree的方式来构造BST树,按顺序输出每一个非root结点的父节点的值. analyse: 构造BST树最坏情况下时间复杂度为O(n),肯定会超时. 注意到只需要输出结点的父节点的值,不需要真的构造BST树. 插到第i

Codeforces Round #316 (Div. 2) B. Simple Game

思路:把n分成[1,n/2],[n/2+1,n],假设m在左区间.a=m+1,假设m在右区间,a=m-1.可是我居然忘了处理1,1这个特殊数据.被人hack了. 总结:下次一定要注意了,提交前一定要看下边界数据,不要急着交. 题目链接:http://codeforces.com/problemset/problem/570/B <pre name="code" class="cpp">#include<bits/stdc++.h> using

Codeforces Round #316 (Div. 2) C. Replacement

题意:给定一个字符串,里面有各种小写字母和' . ' ,无论是什么字母,都是一样的,假设遇到' . . ' ,就要合并成一个' .',有m个询问,每次都在字符串某个位置上将原来的字符改成题目给的字符,问每次须要多少次合并次数.才使字符串没有' .. ' 思路:最原始的想法,就是对于每一次询问,都遍历整个字符串.这样时间复杂度o(n*m),就高达10^10方,非常明显会tle. 换下思路,事实上每次询问所改变的字符都会保留到下一次.也就是下一次的次数就会受到上一次的影响,那么我仅仅要就算出第一次的