标签:
http://acm.hdu.edu.cn/showproblem.php?pid=5156
BC#25的C题。
题意是:给出一颗大小为n的树,以1为根,然后给出m次染色,每次将节点u加上一种颜色(一个节点可以有多个颜色)。
最后查询树上每个节点对应子树上包含的不同颜色数量。
当时这场比赛没有做,回来看一下题目,没看标解就试着敲了一遍,于是解题思路从一开始就走上了不归路。
标解是O(n+m)的方法,主要思路是将问题转为:一次染色表示将u到根节点的路径都染上这种颜色。
但这样做需要去重,因为如果u和v都被染过某种颜色,那么他们的lca及其父链会被多染一次这种颜色。
于是用到了离线tarjan,一次对所有两两相同颜色节点的lca去重(减去1次染色)。
排序的地方都用基数排序,于是复杂度则为线性的。
这次做的时候没有想到上述标解线性的算法,
想到的方法是直接对树做一遍dfs序,所有的节点都替换为该节点上染的颜色序列,然后即可求出每个节点对应的子树的在dfs序列中的区间(子树上的节点在dfs序中一定是连续的)。
于是这道题被赤裸裸地转化为区间不同数字数量的查询,参见SPOJ - DQUERY : http://www.spoj.com/problems/DQUERY/。
关于这个问题有两种方法,一种是离线树状数组,一种是在此基础上改为在线的主席树,具体不多展开,有众多题解可以参考。
主席树的方法在这里会妥妥地MLE, 于是这里采用离线树状数组的方法。
由于这个序列的长度最多为m,所以就实现了一个mlogm的算法(目测好容易超时啊 = =)。
最后用快速读入,并且将每个节点上重复的颜色去重了才过掉这道题,2400ms,危险飘过。
回头看代码的时候发现记录上一个数字出现位置的时候不需要用map,直接用数组就可以,于是优化了一下,最后900ms。
1 #include <iostream> 2 #include <sstream> 3 #include <ios> 4 #include <iomanip> 5 #include <functional> 6 #include <algorithm> 7 #include <vector> 8 #include <string> 9 #include <list> 10 #include <queue> 11 #include <deque> 12 #include <stack> 13 #include <set> 14 #include <map> 15 #include <cstdio> 16 #include <cstdlib> 17 #include <cmath> 18 #include <cstring> 19 #include <climits> 20 #include <cctype> 21 using namespace std; 22 #define XINF INT_MAX 23 #define INF 0x3FFFFFFF 24 #define MP(X,Y) make_pair(X,Y) 25 #define PB(X) push_back(X) 26 #define REP(X,N) for(int X=0;X<N;X++) 27 #define REP2(X,L,R) for(int X=L;X<=R;X++) 28 #define DEP(X,R,L) for(int X=R;X>=L;X--) 29 #define CLR(A,X) memset(A,X,sizeof(A)) 30 #define IT iterator 31 #define RIT reverse_iterator 32 typedef long long ll; 33 typedef unsigned long long ull; 34 typedef pair<int,int> PII; 35 typedef vector<PII> VII; 36 typedef vector<int> VI; 37 #define X first 38 #define Y second 39 #define lson(X) ((X)<<1) 40 #define rson(X) ((X)<<1|1) 41 42 int Scan() 43 { 44 int res, ch=0; 45 while(!(ch>=‘0‘&&ch<=‘9‘)) ch=getchar(); 46 res=ch-‘0‘; 47 while((ch=getchar())>=‘0‘&&ch<=‘9‘) 48 res=res*10+ch-‘0‘; 49 return res; 50 } 51 52 void Out(int a) 53 { 54 if(a>9) 55 Out(a/10); 56 putchar(a%10+‘0‘); 57 } 58 59 60 int c[500010]; 61 int N; 62 63 void init(int n) { 64 N = n; 65 REP(i,N+1) c[i]=0; 66 } 67 68 void add(int i, int x) { 69 while(i<=N) { 70 c[i]+=x; 71 i+=i&-i; 72 } 73 } 74 75 int sum(int i) { 76 int r=0; 77 while(i) { 78 r+=c[i]; 79 i-=i&-i; 80 } 81 return r; 82 } 83 84 int a[500010]; 85 86 int ans[50000]; 87 VII query[500010]; 88 89 VI Map[50000]; 90 int vis[50000]; 91 int s[50000], e[50000]; 92 VI vec[50000]; 93 94 int tot=0; 95 void dfs(int u) { 96 vis[u]=1; 97 s[u]=tot; 98 REP(i,vec[u].size()) a[tot++]=vec[u][i]; 99 REP(i,Map[u].size()) { 100 int v=Map[u][i]; 101 if(!vis[v]) dfs(v); 102 } 103 e[u]=tot-1; 104 } 105 106 int mp[100001]; // 此处如果改用map会慢很多 107 108 int main() { 109 int n,m,l,r,u,v; 110 while(~scanf("%d%d",&n,&m)) { 111 REP(i,50000) Map[i].clear(); 112 REP(i,50000) vec[i].clear(); 113 REP(i,n-1) { 114 u=Scan(); v=Scan(); 115 u--; v--; 116 Map[u].PB(v); 117 Map[v].PB(u); 118 } 119 REP(i,m) { 120 u=Scan(); v=Scan(); 121 u--; 122 vec[u].PB(v); 123 } 124 /* 对一个节点上的颜色去重 125 REP(i,n) { 126 sort(vec[i].begin(),vec[i].end()); 127 vec[i].erase(unique(vec[i].begin(),vec[i].end()),vec[i].end()); 128 } 129 */ 130 tot=1; 131 CLR(vis,0); 132 dfs(0); 133 134 REP(i,tot) query[i].clear(); 135 REP(i,n) query[s[i]].PB(MP(e[i],i)); 136 //map<int,int> mp; 137 CLR(mp,-1); 138 init(tot); 139 DEP(i,tot,1) { 140 add(i,1); 141 if(mp[a[i]]!=-1) add(mp[a[i]],-1); 142 REP(j,query[i].size()) { 143 ans[query[i][j].Y] = sum(query[i][j].X); 144 } 145 mp[a[i]]=i; 146 } 147 REP(i,n) { 148 if(i) putchar(‘ ‘); 149 Out(ans[i]); 150 } 151 putchar(‘\n‘); 152 } 153 return 0; 154 }
HDU 5156 - Harry and Christmas tree (dfs序+离线树状数组)
标签:
原文地址:http://www.cnblogs.com/curs0r/p/4317001.html