CodeForces 825G Educational Round #25 G :建树选根大法+O1大法+iostream解绑了还是慢

题意:给出一棵无向的树(n<=1e6),开始树的所有vertex都是白色的,然后给出q(<=1e6)次询问,询问分为1、2两种:1:将index号节点涂成黑色。2:询问index号节点到所有的小黑点的简单路径中的标号最小的那个点(包括起点和小黑点)。题目保证第一次询问是1类型的。

题解:如果我们随便选取root,那么询问的时候,我们要找到这个点到所有小黑点的路径(LCA),但是这样显然会超时的。所以我们换一种建树姿势。由于第一个询问必然是1类型,那么我们就把第一次询问的那个变黑的点作为root,看一下这样有什么好处:假设我们树上已经有了一些小黑点了,那么当我们遇到2类型询问index的时候,我们考虑index到所有的小黑点的简单路径:首先是root,路径就是index到根,其次考虑非root的小黑点,分成两种情况:1、lca(小黑点,index)==root,那么index到这个小黑点的简单路径是index-root-小黑点。2、lca(index,小黑点)!=root,那么index到小黑点的路径是index-lca-小黑点,而lca必然在index-root的路径上。而因为root是小黑点,所以index-root上的最小值一定会被考虑,而lca-root必然是index-root的一段,所以说index-lca-小黑点这条路可以拆成index-root和root-小黑点。(因为lca-root这条路径必然会被考虑,所以可以把index-lca-小黑点的检测范围拓展一下,对答案毫无影响)。因此我们只需要记录下树上每个vertex到root的路径上所有点标号最小的那一个(先求Deep,然后跑DFS),之后就可以O(1)出答案了。

注意:1、本题强制在线,需要记录last=最后一次2询问的ans,并且每次询问的点的index=(input+last)%n+1。

2、cin、cout慢的出翔,解绑流之后效率依然不足够,乖乖的用scanf/printf吧。

Code:

#include<bits/stdc++.h>
using namespace std;
#define MAX 1000006
#define INF n+1
int n,q;
vector<int> E[MAX];
int minNum[MAX];
int que[MAX*5];
int deep[MAX];
int root;
int last = 0;
int ans ;
void init(){
 cin>>n>>q;
 for (int i=1;i<n;i++){
  int u,v;
  scanf("%d%d",&u,&v);
  E[u].push_back(v);
  E[v].push_back(u);
 }
 scanf("%d%d",&root,&root);
 root = root%n+1;
 ans = root;
 q--;
 for (int i=0;i<=n;i++){
  minNum[i] = i;
 }
} 
void initBuild(){
 int l =0,r=1;
 que[1] = root;
 deep[root] = 1;
 while (l<r){
  l++;
  int a = que[l];
  for (vector<int>::iterator it = E[a].begin();it!=E[a].end();it++){
   if (deep[(*it)]==0){
    deep[(*it)] = deep[a]+1;
    r++;
    que[r] = (*it);
   }
  }
 }
}
void build (){
 initBuild();
 int l=0,r=1;
 que[1]=root;
 minNum[root] = root;
 while (l<r){
  l++;
  int a = que[l];
  int temp = minNum[a];
  for (vector<int>::iterator it = E[a].begin();it!=E[a].end();it++){
   if (deep[(*it)]>deep[a]){
    minNum[(*it)] = min(temp,minNum[(*it)]);
    r++;
    que[r] = (*it);
   }
  }
 }
}
void solve(){
 while (q--){
  int flag,index;
  scanf("%d%d",&flag,&index);
  index = (index+last)%n+1;
  if (flag==1){
   ans = min (ans,minNum[index]);
  }else{
   last  = min(ans,minNum[index]);
   printf("%d\n",last);
  }
 }
}
int main(){
 init(); 
 build();
 solve();
 return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值