http://acm.hdu.edu.cn/showproblem.php?pid=4358
题意:
给你一颗树,n个节点,每个有其权值。给一个k。
q次询问,每次询问 以x为根节点的子树里,有多少种权值恰好出现次数为k。
我们先求个dfs序,把树型结构转为线性数组。
那么题目变成q次查询,每次查询区间L【x】,R【x】之间有多少个权值,出现的次数恰好为k
而本题可以用离线的做法,先把所有查询的区间按右端点排序。
pos[x][j],代表值为x的数在J次出现的位置
接下来是用dfs序数组建树的过程,显然当我们用1-i 的节点建树之后,i+1之后的节点不会影响 所有【 以i为右端点的区间查询】
因此我们 边插入 边查询【j,i】(j<=i) 的答案
插入一个x,假设其出现次数为y,当y<k时,显然对答案无影响,当y>=k时
会影响的是(pos[x][y-k],pos[x][y-k+1]】这个区间所有的数,显然这个区间的任意点z,构成的区间【z,当前最右边】这部分的答案会+1,
同时,当y>k的情况下,还会导致【p[x][y-k-1],p[x][y-k]】这个区间的任意点z,构成的区间【z,到最右边】这部分答案-1 (因为y超过了k嘛)
因此我们从左到右 依次把dfs序数组的数插进树状数组,每次插入后,可以查询到【j,i】的正确答案,插入n次的过程可以查到所有区间的询问。
。。对区间+1 -1部分直接用线段树的区间更新比较好写咯
线段树写法:
#include <cstdio> #include <cmath> #include <cstring> #include <string> #include <cstdio> #include <algorithm> #include <iostream> #include <queue> #include <map> #include <vector> #define inf 0x7fffffff #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 50005; using namespace std; int n,m; const int N = 100005 ; struct tree { int sum[4*N], add[4*N] ; void pushDown(int i, int l, int r) { if(add[i] != 0) { int mid = (l + r) >> 1; add[i << 1] += add[i]; sum[i << 1] += (mid - l + 1) * add[i]; //[l, mid]代表左儿子区间 add[i << 1 | 1] += add[i]; sum[i << 1 | 1] += (r - mid) * add[i]; //[mid + 1, r]代表右儿子区间 add[i] = 0; } } void update(int i, int l, int r, int ql, int qr, int val) //更新区间为qlqr,当前区间为l,r,代表当前区间和的节点为i,更新值为val, { if(l > qr || ql > r) return ; if(l >= ql && r <= qr) { sum[i] += (r - l + 1) * val; add[i] += val; return ; } pushDown(i, l, r); int mid = (l + r) >> 1; update(i << 1, l, mid, ql, qr, val); update(i << 1 | 1, mid + 1, r, ql, qr, val); sum[i] = sum[i << 1] + sum[i << 1 | 1]; } int query(int i, int l, int r, int ql, int qr) //查询区间为qlqr,当前区间为l,r,代表当前区间和的节点为i { if(l > qr || ql > r) return 0; if(l >= ql && r <= qr) return sum[i]; pushDown(i, l, r); int mid =( l + r) >> 1; return query(i << 1, l, mid, ql, qr) + query(i << 1 | 1, mid + 1, r, ql, qr); } }; tree tp; vector < vector<int> > mp(100000+50); int id; int in[100000+50],out[100000+50]; int ren[100005]; int vis[100005]; void dfs1(int x) { in[x]=++id; ren[id]=x; vis[x]=1; int i; for (i=0; i<mp[x].size(); i++) { int v=mp[x][i]; if (vis[v])continue; dfs1(v); } out[x]=id; } int dis[100005]; int aa[100005]; vector<int>pos[100005]; struct node { int x,y,id; node() {} node(int a,int b) { x=a,y=b; } }; node atm[100005]; bool cmp(node a,node b) { return a.y<b.y; } int ans[100005]; int main() { int t; cin>>t; int cnt=1; while(t--) { int k,i,j; int x,y; memset(vis,0,sizeof vis); memset(tp.sum,0,sizeof tp.sum); memset(tp.add,0,sizeof tp.add); id=0; for (i=1; i<=n; i++) mp[i].clear(); cin>>n>>k; for (i=1; i<=n; i++) scanf("%d",&aa[i]),dis[i]=aa[i]; sort(dis+1,dis+1+n); int sz=unique(dis+1,dis+1+n)-dis-1; for (i=1; i<=sz; i++) pos[i].clear(),pos[i].push_back(0); //初始化第0个位置 for (i=1; i<=n-1; i++) { scanf("%d%d",&x,&y); mp[x].push_back(y); mp[y].push_back(x); } dfs1(1); //求dfs序 int q; cin>>q; for ( i=1; i<=q; i++) { scanf("%d",&x); atm[i].x=in[x]; //管辖区间范围 atm[i].y=out[x]; atm[i].id=i; } sort(atm+1,atm+1+n,cmp); j=1; for ( i=1; i<=n; i++) { x=ren[i ]; x=lower_bound(dis+1,dis+1+sz,aa[x])-dis; //离散化对应下标 pos[x].push_back(i); y=pos[x].size()-1; //出现过y次 if (y>=k) { tp.update(1,1,n,pos[x][y-k]+1,pos[x][y-k+1],1); if (y>k) //答案-1的区间 { tp.update(1,1,n,pos[x][y-k-1]+1,pos[x][y-k],-1); } } for (; j<=q&&atm[j].y==i; j++) //处理i为右端点的询问 ans[atm[j].id]=tp.query(1,1,n,atm[j].x,atm[j].x);//printf("%d",tp.get(atm[j].x)); } printf("Case #%d:\n",cnt++); for (i=1; i<=q; i++) printf("%d\n",ans[i]); if (t)printf("\n"); } return 0; }
树状数组写法:
#include <cstdio> #include <cmath> #include <cstring> #include <string> #include <cstdio> #include <algorithm> #include <iostream> #include <queue> #include <map> #include <vector> #define inf 0x7fffffff #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 50005; using namespace std; int n,m; struct tree { int tree[100000+5]; int lowbit(int x) { return x&-x; } void add(int x,int value) { for (int i=x; i<=n; i=i+lowbit(i)) { tree[i]+=value; } } int get(int x) { int sum=0; for (int i=x; i; i-=lowbit(i)) { sum+=tree[i]; } return sum; } }; tree tp; vector < vector<int> > mp(100000+50); int id; int in[100000+50],out[100000+50]; int ren[100005]; int vis[100005]; void dfs1(int x) { in[x]=++id; ren[id]=x; vis[x]=1; int i; for (i=0; i<mp[x].size(); i++) { int v=mp[x][i]; if (vis[v])continue; dfs1(v); } out[x]=id; } int dis[100005]; int aa[100005]; vector<int>pos[100005]; struct node { int x,y,id; node(){} node(int a,int b) { x=a,y=b; } }; node atm[100005]; bool cmp(node a,node b) { return a.y<b.y; } int ans[100005]; int main() { int t; cin>>t; int cnt=1; while(t--) { int k,i,j; int x,y; memset(vis,0,sizeof vis); memset(tp.tree,0,sizeof tp.tree); id=0; for (i=1; i<=n; i++) mp[i].clear(); cin>>n>>k; for (i=1; i<=n; i++) scanf("%d",&aa[i]),dis[i]=aa[i]; sort(dis+1,dis+1+n); int sz=unique(dis+1,dis+1+n)-dis-1; for (i=1; i<=sz; i++) pos[i].clear(),pos[i].push_back(0); //初始化第0个位置 for (i=1; i<=n-1; i++) { scanf("%d%d",&x,&y); mp[x].push_back(y); mp[y].push_back(x); } dfs1(1); //求dfs序 int q; cin>>q; for ( i=1; i<=q; i++) { scanf("%d",&x); atm[i].x=in[x]; //管辖区间范围 atm[i].y=out[x]; atm[i].id=i; } sort(atm+1,atm+1+n,cmp); j=1; for ( i=1; i<=n; i++) { x=ren[i ]; x=lower_bound(dis+1,dis+1+sz,aa[x])-dis; //离散化对应下标 pos[x].push_back(i); y=pos[x].size()-1; //出现过y次 if (y>=k) { tp.add(pos[x][y-k]+1,1); //答案+1的区间 tp.add(pos[x][y-k+1]+1,-1); if (y>k) //答案-1的区间 { tp.add(pos[x][y-k-1]+1,-1); tp.add(pos[x][y-k]+1,1); } } for (;j<=q&&atm[j].y==i;j++) //处理i为右端点的询问 ans[atm[j].id]=tp.get(atm[j].x);//printf("%d",tp.get(atm[j].x)); } printf("Case #%d:\n",cnt++); for (i=1;i<=q;i++) printf("%d\n",ans[i]); if (t)printf("\n"); } return 0; }