ACM-ICPC 2018 沈阳赛区网络预赛 J. Ka Chang

ACM-ICPC 2018 沈阳赛区网络预赛 J. Ka Chang
题意:两个操作,1操作将深度为x的所有点加权值k,2操作询问x的子树的权值和。
q<=1e5,n<=1e5。
思路:可以考虑两种思路,第一种用树状数组维护每个dfs序对应点的权值,当操作1输入x,k时则把深度为x的所有点的所对应dfs序再在树状数组中都加k,然后查询子树x的权值和时即ask(out[x])-ask(in[x]-1),时间复杂度为o(q * n * log(n))。
第二种开一个向量w[deep]存深度为deep的点的dfs序,然后当操作1时直接开一个数组sum[deep]代表深度为deep的点的权值,然后对于每个操作2,对于所有深度都进行一次二分,因为向量存dfs序时是有序的,所以可以二分找在[in[x],out[x]]这个范围的深度为deep的点的个数,然后乘以sum[deep]再累加即可,时间复杂度为o(q * n * log(n))。
这两个方法一个操作1是o(1),一个操作2是o(1),但是总的复杂度超时,于是可以分块,假设深度为deep的点的个数小于t的化就用第一种方法,时间复杂度为o(q * t * log(n)),大于等于t用第二种方法,由于点的个数大于等于t的的深度最多为n/t,所以时间复杂度为
o(q * (n/t) * log(n))。然后当t=sqrt(n)时综合时间复杂度最小,为o(q * log(n) * sqrt(n))。
极限时间复杂度大约为1e8。
这个时间复杂度将n优化成了根号n,虽然极限数据会T,但是可能因为很难造出这种极限数据或者根本没有这种极限数据所以A了,或者1e8也能跑。

#include<iostream>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAX_N=101000;
vector<int>v[MAX_N];
vector<int>w[MAX_N],sw[MAX_N];
bool vis[MAX_N];
int in[MAX_N],out[MAX_N];
int cnt=0;
void dfs(int x,int deep){
 vis[x]=true;
 in[x]=++cnt;
 w[deep].push_back(x);
 sw[deep].push_back(cnt);
 for(int i=0;i<v[x].size();i++){
  int y=v[x][i];
  if(vis[y])
  continue;
  dfs(y,deep+1);
 }
 out[x]=cnt;
 //cout<<x<<" "<<in[x]<<" "<<out[x]<<" !!!\n";
}
int n,num[MAX_N];
long long a[MAX_N],sum[MAX_N];
void add(int p,long long x){
 while(p<=n){
  a[p]+=x;
  p+=p&-p;
 }
}
long long ask(int p){
 long long ans=0;
 while(p){
  ans+=a[p];
  p-=p&-p;
 }
 return ans;
} 
int main(void){
 int q,i,j,x,y,op;
 scanf("%d%d",&n,&q);
 for(i=1;i<n;i++){
  scanf("%d%d",&x,&y);
  v[x].push_back(y);
  v[y].push_back(x);
 }
 dfs(1,0);
 int tot=0;
 int nn=sqrt(n);
 for(i=0;i<=n;i++){
  if(w[i].size()>=nn)
  num[++tot]=i;
 }
 long long k;
 //cout<<nn<<" nn\n";
 for(i=0;i<q;i++){
  scanf("%d",&op);
  if(op==1){
   scanf("%d%lld",&x,&k);
   if(w[x].size()<nn){
    for(j=0;j<w[x].size();j++){
     add(in[w[x][j]],k);
    }
   }
   else{
    sum[x]+=k;
   }
  }
  else{
   scanf("%d",&x);
   long long res=ask(out[x])-ask(in[x]-1);
   for(j=1;j<=tot;j++){
    y=num[j];
    res+=sum[y]*(long long)(upper_bound(sw[y].begin(),sw[y].end(),out[x])-lower_bound(sw[y].begin(),sw[y].end(),in[x]));
   }
   printf("%lld\n",res);
  }
 }
 return 0;
}
发布了62 篇原创文章 · 获赞 6 · 访问量 1941
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章