NOIP 2016 Day1 T2 天天爱跑步

题目大意

给定一个n个节点,n?1条边的树。有m个玩家,第i个玩家从xi走树上最短路径到yi。玩家第0秒在自己的起点上,然后每秒移动一条边,移动到终点后结束移动。 
每个节点上有一个观察员以及权值wi。如果有一个玩家在其移动的第wi秒恰好到达这个点,那么这个点上的观察员就会观察到他(如果这个点是终点,且玩家在wi秒之前到达不算)。 
求每个点上的观察员分别观察到了多少个玩家。

1≤n≤299998,1≤m≤299998

题目分析

我们将每条路径拆成从出发点到lca,和从lca的下一个点到结束点两段。 
对于第一段,如果在点i时被观察到,我们可以写出deep(x)?deep(i)=wi。因此我们对所有deep(x)开权值线段树(下标为DFN(x),动态开点),在每个玩家路径lca处挂一个在deep(x)的权值线段树中DFN(x)位置权值+1的标记。然后每访问一个点就要先处理该点的所有标记,在deep(i)+wi的权值线段树里面查询子树的权值和。但是因为在lca上面的点就不能观察到这个点,因此退出一个点时要撤销上面的标记。 
对于第二段,如果点i时被观察到了,我们可以写出deep(x)+deep(i)?2deep(lca(x,i))=wi,和上面类似,那么我们对所有路径的deep(x)?2deep(lca(x,y))开权值线段树(依然下标为DFN(x),动态开点)。然后将标记挂在lca靠y的儿子那里(为了避免算重)。然后在wi?deep(i)的权值线段树中查询。 
这样就要实现一个倍增和常数比较大的权值线段树, 时间复杂度O(nlog2n)。 
听说CCF老爷机要卡log算法的常,那我也没办法了。 
update:将我的权值线段树改成桶就可以O(n)了QwQ。

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstdio>
  4 #include<cctype>
  5 #include<cmath>
  6 using namespace std;
  7 int buf[30];
  8 const int N=300050;
  9 const int LGN=19;
 10 const int M=300050;
 11 const int E=N<<1;
 12 const int Q=M<<1;
 13 struct query{
 14     bool tp;
 15     int key,x;
 16 }qy[Q];
 17 int qnxt[Q];
 18 struct D{
 19     int key,x,y,lab;
 20 }srt[M];
 21 int d;
 22 bool operator<(D a,D b){return a.key<b.key;}
 23 struct segment_tree
 24 {
 25     int sum[N*20],son[N*20][2],root[M];
 26     int tot;
 27     int newnode()
 28     {
 29         sum[++tot]=0;
 30         son[tot][0]=son[tot][1]=0;
 31         return tot;
 32     }
 33     void update(int x){sum[x]=sum[son[x][0]]+sum[son[x][1]];}
 34     void modify(int &x,int y,int l,int r,int delta)
 35     {
 36         if (!x) x=newnode();
 37         if (l==r)
 38         {
 39             sum[x]+=delta;
 40             return;
 41         }
 42         int mid=l+r>>1;
 43         if (y<=mid) modify(son[x][0],y,l,mid,delta);
 44         else modify(son[x][1],y,mid+1,r,delta);
 45         update(x);
 46     }
 47     int query(int x,int st,int en,int l,int r)
 48     {
 49         if (!x) return 0;
 50         if (st==l&&en==r) return sum[x];
 51         int mid=l+r>>1;
 52         if (en<=mid) return query(son[x][0],st,en,l,mid);
 53         else if (mid+1<=st) return query(son[x][1],st,en,mid+1,r);
 54         else return query(son[x][0],st,mid,l,mid)+query(son[x][1],mid+1,en,mid+1,r);
 55     }
 56 }t[2];
 57 int deep[N],last[N],size[N],DFN[N],qlst[N],w[N],ans[N];
 58 int tov[E],nxt[E],fa[N][LGN];
 59 int n,m,tot,idx,lgn,cnt,dif;
 60 void insert(int x,int y){tov[++tot]=y,nxt[tot]=last[x],last[x]=tot;}
 61 void hang(int x,int y){qnxt[y]=qlst[x],qlst[x]=y;}
 62 void dfs(int x)// 处理深度 以及 fa数组
 63 {
 64     DFN[x]=++idx,size[x]=1;
 65     for(int i=last[x],y;i;i=nxt[i])
 66         if((y=tov[i])!=fa[x][0])
 67           fa[y][0]=x,deep[y]=deep[x]+1,dfs(y),size[x]+=size[y];
 68 }
 69 void pre()
 70 {
 71     for(int j=1;(1<<j)<=n;j++)
 72         for(int i=1;i<=n;i++)
 73           fa[i][j]=fa[fa[i][j-1]][j-1];
 74 }
 75 int adjust(int x,int d)
 76 {
 77     for (int i=lgn;i>=0;i--) if (deep[fa[x][i]]>=d) x=fa[x][i];
 78     return x;
 79 }
 80 int lca(int x,int y)
 81 {
 82     if (deep[x]>deep[y]) swap(x,y);
 83     y=adjust(y,deep[x]);
 84     if (x==y) return x;
 85     for (int i=lgn;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
 86     return fa[x][0];
 87 }
 88 void proc()
 89 {
 90     sort(srt+1,srt+1+d);
 91     dif=0,srt[0].key=-n*2;
 92     for (int i=1;i<=d;i++)
 93     {
 94         dif+=srt[i].key!=srt[i-1].key;
 95         srt[i].lab=dif;
 96         qy[++cnt].tp=1,qy[cnt].key=dif,qy[cnt].x=srt[i].y;
 97         hang(srt[i].x,cnt);
 98     }
 99 }
100 query tmp;
101 int search(int aim)
102 {
103     int ret=0;
104     int l=1,r=d,mid;
105     while (l<=r)
106     {
107         mid=l+r>>1;
108         if (srt[mid].key<=aim) ret=mid,l=mid+1;
109         else r=mid-1;
110     }
111     if (srt[ret].key==aim) return srt[ret].lab;
112     return 0;
113 }
114 void calc(int x)
115 {
116     for (int i=qlst[x];i;i=qnxt[i])
117     {
118         tmp=qy[i];
119         t[tmp.tp].modify(t[tmp.tp].root[tmp.key],DFN[tmp.x],1,n,1);
120     }
121     for (int i=last[x],y;i;i=nxt[i])
122         if ((y=tov[i])!=fa[x][0]) calc(y);
123     ans[x]=0;
124     if (w[x]+deep[x]<=n) ans[x]+=t[0].query(t[0].root[w[x]+deep[x]],DFN[x],DFN[x]+size[x]-1,1,n);
125     int f=search(deep[x]-w[x]);
126     if (f) ans[x]+=t[1].query(t[1].root[f],DFN[x],DFN[x]+size[x]-1,1,n);
127     for (int i=qlst[x];i;i=qnxt[i])
128     {
129         tmp=qy[i];
130         t[tmp.tp].modify(t[tmp.tp].root[tmp.key],DFN[tmp.x],1,n,-1);
131     }
132 }
133 int main()
134 {
135     scanf("%d%d",&n,&m);
136     for (int i=1,x,y;i<n;i++)
137     {
138         scanf("%d%d",&x,&y);
139         insert(x,y),insert(y,x);
140     }
141     fa[1][0]=0,deep[1]=1,dfs(1),pre();
142     for(int i=1;i<=n;i++) scanf("%d",&w[i]);
143     for(int x,y,z;m--;)
144     {
145         scanf("%d%d",&x,&y);z=lca(x,y);
146         qy[++cnt].tp=0,qy[cnt].key=deep[x],qy[cnt].x=x;
147         hang(z,cnt);
148         if(!(DFN[y]<=DFN[x]&&DFN[x]<=DFN[y]+size[y]-1))
149          srt[++d].key=deep[z]*2-deep[x],srt[d].x=adjust(y,deep[z]+1),srt[d].y=y;
150     }
151     proc(),calc(1);
152     for (int i=1;i<n;i++) printf("%d ",ans[i]);
153     printf("%d\n",ans[n]);
154     return 0;
155 }
时间: 2024-10-10 00:41:34

NOIP 2016 Day1 T2 天天爱跑步的相关文章

Noip 2016 Day1 题解

老师让我们刷历年真题, 然后漫不经心的说了一句:“你们就先做做noip2016 day1 吧” ...... 我还能说什么,,,,,老师你这是明摆着伤害我们啊2333333333 预计分数:100+25+24 实际分数:100+25+12 T1:玩具谜题 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业. 有一天, 这些玩具小人把小南的眼镜藏了起来. 小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外.如下图: 这时singer告诉小南一个谜題: “眼镜藏在我左数第3个玩具小

NOIP 2011 Day1 T2 选择客栈

1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<string> 5 #include<cstring> 6 #include<algorithm> 7 #include<vector> 8 #include<queue> 9 #include<stack> 10 #include<iomanip>

NOIP 2017 Day1 T2 时间复杂度

luogu题面 大模拟...并不难(然而考场上写挂了) 用 3 个读入函数,解决读入问题 双栈齐发,一个记录使用过的字母,另一个记录复杂度贡献情况 用 bre 表示当前跳出循环的层数 用 err 表示当前编译状态(是否编译失败) s 和 ans 分别表示当前复杂度和总复杂度 那么当读取到 'F' 时,我们就要做以下几点: 读取循环中使用的字母,并判断其是否被使用过,并将其标记为已使用: 读取循环的起止值,判断其能否被进入,并且处理好跳出循环的层数: 判断该循环对复杂度的贡献情况,处理好 s 和

noip 2016 天天爱跑步

描述 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一棵包含n个结点和n - 1条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从1到n的连续正整数. 现在有m个玩家,第i个玩家的起点为Si,终点为Ti.每天打卡任务开始时,所有玩家 在第0秒 同时从 自己的起点 出发,以 每秒跑一条边 的速度,不间断地沿着最短路径向着 自己的终点 跑去,

Noip 2016 天天爱跑步 【树上倍增+深搜】

题目: 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.?天天爱跑步?是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 个结点和 条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从到的连续正整数. 现在有个玩家,第个玩家的起点为 ,终点为 .每天打卡任务开始时,所有玩家在第秒同时从自己的起点出发, 以每秒跑一条边的速度, 不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务.

[luogu]P1600 天天爱跑步[LCA]

[luogu]P1600 [NOIP 2016]天天爱跑步 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含n个结点和n−1条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从1到n的连续正整数. 现在有m个玩家,第i个玩家的起点为Si?,终点为Ti? .每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发, 以每秒跑一条边的速

天天爱跑步[NOIP2016]

时间限制:2 s   内存限制:512 MB [题目描述] 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一棵包含n个结点和n-1条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达.树上结点编号为从1到n的连续正整数. 现在有m个玩家,第i个玩家的起点为Si,终点为Ti.每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发,以每秒跑一条边的速度,不间断

NOIP 2016 迟来的满贯

17-03-22,雨 17-03-22,一个特别重要的日子 在这一天,本蒻攻克了NOIP 2016最难的一题,D1T2--天天爱跑步 实现了NOIP 2016的AK! YAYAYAYAYAYAY 自然遭到了众巨神的膜拜--他们不屑于做这种弱菜才做的题目--RP又掉了 17-03-22于福州

Noip 2016 Day 1 &amp; Day 2

Day 1 >>> T1 >> 水题直接模拟AC: 考察三个知识点:1.你能不能编程 2.你会不会取模 3.你脑子抽不抽 然而第一次评测还是90,因为当模运算时 " end == 0 " 时需要将 end 改成 n: 代码: 1 #include <bits/stdc++.h> 2 3 const int N = 1e5 + 5 ; 4 5 int n , k , x , y , end ; 6 7 struct node { 8 int id