Wannafly Day2 E 阔力梯的树(树上启发式合并)

题目链接:https://ac.nowcoder.com/acm/contest/4010/E

题目分析:

错误解法:看一眼就知道要用set,于是我一开始非常莽的跑了一发O(n*n*logn)的假算法,过了70%的数据,就是对于每次操作,新添加的树合并到重儿子上,合并完之后,用vector把set里的元素全部复制(提取)出来,再O(n)扫一发vector序列,想想吧,1e5大小的肯定爆TLE了.

 1 #include
 2 #define ll long long
 3 #define rep(i,a,n) for(int i=a;i<=n;i++)
 4 #define per(i,n,a) for(int i=n;i>=a;i--)
 5 #define endl '\n'
 6 #define eps 0.000000001
 7 #define pb push_back
 8 #define mem(a,b) memset(a,b,sizeof(a))
 9 #define IO ios::sync_with_stdio(false);cin.tie(0);
10 using namespace std;
11 const int INF=0x3f3f3f3f;
12 const ll inf=0x3f3f3f3f3f3f3f3f;
13 const int mod=1e9+7;
14 const int maxn=1e5+5;
15 int tot,head[maxn];
16 struct E{
17     int to,next;
18 }edge[maxn<<1];
19 void add(int u,int v){
20     edge[tot].to=v;
21     edge[tot].next=head[u];
22     head[u]=tot++;
23 }
24 int n;
25 int siz[maxn],son[maxn],dep[maxn];
26 void dfs1(int u,int f){
27     dep[u]=dep[f]+1;
28     siz[u]=1;
29     for(int i=head[u];i!=-1;i=edge[i].next){
30         int v=edge[i].to;
31         if(v==f) continue;
32         dfs1(v,u);
33         siz[u]+=siz[v];
34         if(siz[v]>siz[son[u]]) son[u]=v;
35     }
36 }
37 int flag;set<int> s;ll ans[maxn];
38 void count(int u,int f,int val){
39     if(val==1) s.insert(u);
40     else s.erase(u);
41     for(int i=head[u];i!=-1;i=edge[i].next){
42         int v=edge[i].to;
43         if(v==f||v==flag) continue;
44         count(v,u,val);
45     }
46 }
47 void dfs(int u,int f,int keep){
48     for(int i=head[u];i!=-1;i=edge[i].next){
49         int v=edge[i].to;
50         if(v==f||v==son[u]) continue;
51         dfs(v,u,0);
52     }
53     if(son[u]){
54         dfs(son[u],u,1);
55         flag=son[u];
56     }
57     count(u,f,1);
58     vector<int> vec;
59     for(auto it:s) vec.push_back(it);
60     ll sum=0;
61     for(int i=0;i1;i++){
62         sum+=(vec[i+1]-vec[i])*(vec[i+1]-vec[i]);
63     }
64     ans[u]=sum;
65     flag=0;
66     if(!keep){
67         count(u,f,-1);
68     }
69 }
70 int main(){
71     cin>>n;mem(head,-1);
72     rep(i,2,n){
73         int x;cin>>x;
74         add(x,i);add(i,x);
75     }
76     dfs1(1,0);
77     dfs(1,0,0);
78     rep(i,1,n){
79         cout<endl;
80     }
81 }
View Code

 

正确解法:借助树上启发式合并不断传递的思想,对于每次更新的一个数,我们都可以在logn的时间复杂度内解决,具体实现就是先二分找到这个操作数的位置,并取出其相邻的两个数,减去之前的贡献,累加上新增加的贡献即可完成贡献(这一步很妙!!我感觉可以出一下这方面的坑新生hhh),这样总的时间复杂度就是O(n*logn*logn)了。这里对STL中set容器的运用我又加深了一步嘻嘻

  1 #include
  2 #define ll long long
  3 #define rep(i,a,n) for(int i=a;i<=n;i++)
  4 #define per(i,n,a) for(int i=n;i>=a;i--)
  5 #define endl '\n'
  6 #define eps 0.000000001
  7 #define pb push_back
  8 #define mem(a,b) memset(a,b,sizeof(a))
  9 #define IO ios::sync_with_stdio(false);cin.tie(0);
 10 using namespace std;
 11 const int INF=0x3f3f3f3f;
 12 const ll inf=0x3f3f3f3f3f3f3f3f;
 13 const int mod=1e9+7;
 14 const int maxn=1e5+5;
 15 int tot,head[maxn];
 16 struct E{
 17     int to,next;
 18 }edge[maxn<<1];
 19 void add(int u,int v){
 20     edge[tot].to=v;
 21     edge[tot].next=head[u];
 22     head[u]=tot++;
 23 }
 24 int n;
 25 int siz[maxn],son[maxn],dep[maxn];
 26 void dfs1(int u,int f){
 27     dep[u]=dep[f]+1;
 28     siz[u]=1;
 29     for(int i=head[u];i!=-1;i=edge[i].next){
 30         int v=edge[i].to;
 31         if(v==f) continue;
 32         dfs1(v,u);
 33         siz[u]+=siz[v];
 34         if(siz[v]>siz[son[u]]) son[u]=v; 
 35     }
 36 }
 37 int flag;set<int> s;ll ans[maxn],sum;
 38 void in(int num){
 39     set<int>::iterator it=s.lower_bound(num),start=s.begin(),end=s.end();
 40     end--;
 41     auto pre=it,next=it;
 42     if(pre!=start) pre--;
 43     if(next!=end) next++;
 44     if(it!=next&&it!=pre){
 45         sum-=1LL*(*next-*pre)*(*next-*pre);
 46         sum+=1LL*(*next-*it)*(*next-*it);
 47         sum+=1LL*(*it-*pre)*(*it-*pre);
 48     }
 49     else if(it==next&&it!=pre){
 50         sum+=1LL*(*it-*pre)*(*it-*pre);
 51     }
 52     else if(it==pre&&it!=next){
 53         sum+=1LL*(*next-*it)*(*next-*it);
 54     }
 55 }
 56 void out(int num){
 57     set<int>::iterator it=s.lower_bound(num),start=s.begin(),end=s.end();
 58     end--;
 59     auto pre=it,next=it;
 60     if(pre!=start) pre--;
 61     if(next!=end) next++;
 62     if(it!=next&&it!=pre){
 63         sum+=1LL*(*next-*pre)*(*next-*pre);
 64         sum-=1LL*(*next-*it)*(*next-*it);
 65         sum-=1LL*(*it-*pre)*(*it-*pre);
 66     }
 67     else if(it==next&&it!=pre){
 68         sum-=1LL*(*it-*pre)*(*it-*pre);
 69     }
 70     else if(it==pre&&it!=next){
 71         sum-=1LL*(*next-*it)*(*next-*it);
 72     }
 73 }
 74 void count(int u,int f,int val){
 75     if(val==1) s.insert(u),in(u);
 76     else out(u),s.erase(u);
 77     for(int i=head[u];i!=-1;i=edge[i].next){
 78         int v=edge[i].to;
 79         if(v==f||v==flag) continue;
 80         count(v,u,val);
 81     }
 82 }
 83 void dfs(int u,int f,int keep){
 84     for(int i=head[u];i!=-1;i=edge[i].next){
 85         int v=edge[i].to;
 86         if(v==f||v==son[u]) continue;
 87         dfs(v,u,0);
 88     }
 89     if(son[u]){
 90         dfs(son[u],u,1);
 91         flag=son[u];
 92     }
 93     count(u,f,1);
 94     ans[u]=sum;
 95     flag=0;
 96     if(!keep){
 97         count(u,f,-1);sum=0;
 98     }
 99 }
100 int main(){
101     cin>>n;mem(head,-1);
102     rep(i,2,n){
103         int x;cin>>x;
104         add(x,i);add(i,x);
105     }
106     dfs1(1,0);
107     dfs(1,0,0);
108     rep(i,1,n){
109         cout<endl;
110     }
111 }
View Code

 

你可能感兴趣的:(Wannafly Day2 E 阔力梯的树(树上启发式合并))