博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Boring counting HDU - 4358(树上出现k次的数字个数)
阅读量:4135 次
发布时间:2019-05-25

本文共 4713 字,大约阅读时间需要 15 分钟。

In this problem we consider a rooted tree with N vertices. The vertices are numbered from 1 to N, and vertex 1 represents the root. There are integer weights on each vectice. Your task is to answer a list of queries, for each query, please tell us among all the vertices in the subtree rooted at vertice u, how many different kinds of weights appear exactly K times?

Input
The first line of the input contains an integer T( T<= 5 ), indicating the number of test cases.
For each test case, the first line contains two integers N and K, as described above. ( 1<= N <= 10 5, 1 <= K <= N )
Then come N integers in the second line, they are the weights of vertice 1 to N. ( 0 <= weight <= 10 9 )
For next N-1 lines, each line contains two vertices u and v, which is connected in the tree.
Next line is a integer Q, representing the number of queries. (1 <= Q <= 10 5)
For next Q lines, each with an integer u, as the root of the subtree described above.
Output
For each test case, output “Case #X:” first, X is the test number. Then output Q lines, each with a number – the answer to each query.

Seperate each test case with an empty line.

Sample Input
1
3 1
1 2 2
1 2
1 3
3
2
1
3
Sample Output
Case #1:
1
1
1
这个题和eduoj3307的题目一样的,但是这个题需要树形转换成线性的。
离散化,树形转化为数组的线性结构,离线做法,树状数组,排序后边插入边询问等多种处理技巧。
因为这个题目数据量比较大,需要离散化一下。
.在插入到某点i是,第k(1<=k<=i)表示k到i的答案,要明白:
每插入新的一点,改变的只有插入的这个数,设这个数P出现的位置是P0,P1,P2…Pj-k-1,Pj-k,Pj-k+1…Pj-1,
如果询问的左端点在(Pj-k-1,Pj-k],右端点在i,P就凑够K次
现在在Pj=i插入新的数P,就变成如果询问的左端点在(Pj-k,Pj-k+1],P凑够K次,
而左端点在(Pj-k-1,Pj-k]的P出现的次数变成K+1,不是答案,
所以树状数组的操作是(Pj-k-1,Pj-k] 区间-1,(Pj-k,Pj-k+1]区间+1,然后询问左端点的值就好了(因为树状数组上第k位表示k到i的答案)
可见要用到的操作:区间更新,单点询问,用第二类树状数组
代码如下:

#include
#define ll long longusing namespace std;const int maxx=1e5+100;struct node{
int l; int r; int id; bool operator<(const node &a)const{
return a.r>r; }}b[maxx];struct Node{
int to,next;}e[maxx<<1];vector
v[maxx];int head[maxx<<1],in[maxx],out[maxx],a[maxx],pre[maxx];ll c[maxx],ans[maxx];int n,m,k,tot,sign;/*-------------事前准备--------------*/ inline void init(){
memset(head,-1,sizeof(head)); memset(c,0,sizeof(c)); tot=sign=0;}inline void add(int u,int v){
e[tot].to=v,e[tot].next=head[u],head[u]=tot++;}/*---------------dfs---------------*/inline void dfs(int u,int f){
in[u]=++sign; pre[sign]=a[u]; for(int i=head[u];i!=-1;i=e[i].next) {
int to=e[i].to; if(to==f) continue; dfs(to,u); } out[u]=sign;}/*------------树状数组------------*/inline int lowbit(int x){
return x&-x;}inline void update(int cur,int v){
while(cur<=maxx) c[cur]+=v,cur+=lowbit(cur);}inline ll query(int cur){
int sum=0;while(cur>0) sum+=c[cur],cur-=lowbit(cur);return sum;}int main(){
int t,kk=0,x,y; scanf("%d",&t); while(t--) {
scanf("%d%d",&n,&k); init();map
mp; int cnt=0; for(int i=1;i<=n;i++) {
scanf("%d",&a[i]); if(mp[a[i]]==0) {
mp[a[i]]=++cnt; a[i]=cnt; v[cnt].clear(); v[cnt].push_back(0); } else a[i]=mp[a[i]]; } for(int i=1;i
=k) {
if(y>k)//出现次数大于k次了,就需要把之前的标记取消掉,类似于统计区间数字的操作。 {
update(v[x][y-k-1]+1,-1); update(v[x][y-k]+1,1); } update(v[x][y-k]+1,1);//增加新的标记 update(v[x][y-k+1]+1,-1); } while(b[j].r==i&&j<=m) ans[b[j].id]=query(b[j++].l); } printf("Case #%d:\n",++kk); for(int i=1;i<=m;i++) printf("%d\n",ans[i]); if(t) puts(""); }}

eduoj的题目链接:

这是统计区间出现次数为两次的数字个数
代码如下:

#include
#define ll long longusing namespace std;const int maxx=5e5+100;struct Node{
int l; int r; int id; bool operator <(const Node &a)const{
return a.r>r; }}e[maxx];int a[maxx],c[maxx],ans[maxx];vector
v[maxx];int n,m;/*-------------树状数组-------------*/int lowbit(int x){
return x&(-x);}void update(int x,int val){
for(int i=x;i<=n;i+=lowbit(i)) c[i]+=val;}int query(int x){
int ans=0; for(int i=x;i>0;i-=lowbit(i)) ans+=c[i]; return ans;}int main(){
while(~scanf("%d%d",&n,&m)) {
int cnt=0,len; map
mp; memset(c,0,sizeof(c)); for(int i=1;i<=n;i++) {
scanf("%d",&a[i]); if(!mp[a[i]]) {
mp[a[i]]=++cnt; a[i]=cnt; v[cnt].clear(); v[cnt].push_back(0); } else a[i]=mp[a[i]]; } for(int i=1;i<=m;i++) {
scanf("%d%d",&e[i].l,&e[i].r); e[i].id=i; } sort(e+1,e+1+m); for(int i=1,pos=1;i<=n;i++) {
int u=a[i]; v[u].push_back(i); int len=v[u].size()-1; if(len>=2) {
if(len>2)//消除上一次含K个U值的区间更新 {
update(v[u][len-3]+1,-1); update(v[u][len-2]+1,1); } update(v[u][len-2]+1,1); update(v[u][len-1]+1,-1); } while(e[pos].r==i) {
ans[e[pos].id]=query(e[pos].l); pos++; } } for(int i=1;i<=m;i++) printf("%d\n",ans[i]); } return 0;}

努力加油a啊,(o)/~

转载地址:http://oztvi.baihongyu.com/

你可能感兴趣的文章
hdu 4280
查看>>
禁止使用类的copy构造函数和赋值操作符
查看>>
C++学习路线
查看>>
私有构造函数
查看>>
组队总结
查看>>
TitledBorder 设置JPanel边框
查看>>
DBCP——开源组件 的使用
查看>>
抓包工具
查看>>
海量数据相似度计算之simhash和海明距离
查看>>
DeepLearning tutorial(5)CNN卷积神经网络应用于人脸识别(详细流程+代码实现)
查看>>
DeepLearning tutorial(6)易用的深度学习框架Keras简介
查看>>
DeepLearning tutorial(7)深度学习框架Keras的使用-进阶
查看>>
流形学习-高维数据的降维与可视化
查看>>
Python-OpenCV人脸检测(代码)
查看>>
python+opencv之视频人脸识别
查看>>
人脸识别(OpenCV+Python)
查看>>
6个强大的AngularJS扩展应用
查看>>
网站用户登录系统设计——jsGen实现版
查看>>
第三方SDK:讯飞语音听写
查看>>
第三方SDK:JPush SDK Eclipse
查看>>