A Simple Task

因为字符集大小只有 \(26\),考虑直接在线段树每个节点维护当前区间的字符个数。排序时直接暴力区间赋值,复杂度会有 \(26\) 的常数。

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include<iostream>
using namespace std;
const int N=1e5+10;
int n,q;
#define ls u<<1
#define rs u<<1|1
string s;
int tree[N*4][26],tag[N*4];
void push_up(int u){
for(int i=0;i<26;i++)
tree[u][i]=tree[ls][i]+tree[rs][i];
return;
}
void build(int u,int l,int r){
tag[u]=-1;
if(l==r){
tree[u][s[l]-'a']++;
return;
}
int mid=(l+r)/2;
build(ls,l,mid);
build(rs,mid+1,r);
push_up(u);
return;
}
void push_down(int u,int l,int r){
if(!~tag[u])
return;
int mid=(l+r)/2;
for(int i=0;i<26;i++)
tree[ls][i]=tree[rs][i]=0;
tree[ls][tag[u]]=mid-l+1,tree[rs][tag[u]]=r-mid;
tag[ls]=tag[rs]=tag[u];
tag[u]=-1;
return;
}
void modify(int u,int l,int r,int x,int y,int k){
if(x<=l&&y>=r){
for(int i=0;i<26;i++)
tree[u][i]=0;
tree[u][k]=r-l+1;
tag[u]=k;
return;
}
int mid=(l+r)/2;
push_down(u,l,r);
if(x<=mid)
modify(ls,l,mid,x,y,k);
if(y>mid)
modify(rs,mid+1,r,x,y,k);
push_up(u);
return;
}
int query(int u,int l,int r,int x,int y,int k){
if(x<=l&&y>=r)
return tree[u][k];
int mid=(l+r)/2,res=0;
push_down(u,l,r);
if(x<=mid)
res+=query(ls,l,mid,x,y,k);
if(y>mid)
res+=query(rs,mid+1,r,x,y,k);
return res;
}
char query_ans(int u,int l,int r,int x){
if(l==r)
for(int i=0;i<26;i++)
if(tree[u][i])
return 'a'+i;
int mid=(l+r)/2;
push_down(u,l,r);
if(x<=mid)
return query_ans(ls,l,mid,x);
else
return query_ans(rs,mid+1,r,x);
}
int a[26];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>q>>s;
s=" "+s;
build(1,1,n);
int l,r,k;
while(q--){
cin>>l>>r>>k;
if(k){
for(int i=0;i<26;i++)
a[i]=query(1,1,n,l,r,i);
for(int i=0;i<26;i++){
if(a[i])
modify(1,1,n,l,l+a[i]-1,i);
l+=a[i];
}
}
else{
for(int i=0;i<26;i++)
a[i]=query(1,1,n,l,r,i);
for(int i=25;i>=0;i--){
if(a[i])
modify(1,1,n,l,l+a[i]-1,i);
l+=a[i];
}
}
}
for(int i=1;i<=n;i++)
printf("%c",query_ans(1,1,n,i));
return 0;
}

[HEOI2012] 采花

离线,树状数组维护之前出现两次及以上的颜色数,每次出现 \(2\) 次后在之前的位置加 \(1\),并且把之前的之前加的撤销。
code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include<cstdio>
#include<algorithm>
template<typename T>
void read(T &x){
x=0;
char ch=getchar();
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){
read(x);
read(args...);
return;
}
#define lowbit(x) (x&-x)
const int N=2e6+10;
int n,c,m,x[N],tree[N];
void modify(int x,int k){
for(int i=x;i<=n;i+=lowbit(i))
tree[i]+=k;
return;
}
int query(int x){
int res=0;
for(int i=x;i>0;i-=lowbit(i))
res+=tree[i];
return res;
}
struct Query{
int l,r,id;
bool operator<(const Query &x)const{
return r<x.r;
}
}q[N];
int pre1[N],pre2[N],ans[N];
int main(){
read(n,c,m);
for(int i=1;i<=n;i++)
read(x[i]);
for(int i=1;i<=m;i++)
read(q[i].l,q[i].r),q[i].id=i;
std::sort(q+1,q+1+m);
int now=1;
for(int i=1;i<=m;i++){
while(now<=q[i].r){
int temp=x[now];
if(pre1[temp]){
modify(pre1[temp],1);
if(pre2[temp])
modify(pre2[temp],-1);
pre2[temp]=pre1[temp];
}
pre1[temp]=now;
now++;
}
ans[q[i].id]=query(q[i].r)-query(q[i].l-1);
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}

[Ynoi2012] NOIP2015 充满了希望

注意到,任何情况下,查询的答案要么是 \(0\),要么是一个确定的值(即有没有被修改过),这个确定的值必然来自 2 操作。所以维护时间戳,利用树状数组查询区间和。
code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include<iostream>
#include<vector>
using namespace std;
#define ls u<<1
#define rs u<<1|1
#define lowbit(x) x&-x
typedef long long ll;
const int N=1e6+10;
int n,m,q,opt[N];
class segtree{
private:
int tree[N*4];
void push_down(int u){
if(!tree[u])
return;
tree[ls]=tree[rs]=tree[u];
tree[u]=0;
return;
}
public:
void modify(int u,int l,int r,int x,int y,int k){
if(x<=l&&y>=r){
tree[u]=k;
return;
}
int mid=(l+r)/2;
push_down(u);
if(x<=mid)
modify(ls,l,mid,x,y,k);
if(y>mid)
modify(rs,mid+1,r,x,y,k);
return;
}
int query(int u,int l,int r,int x){
if(tree[u]||l==r)
return tree[u];
int mid=(l+r)/2;
push_down(u);
if(x<=mid)
return query(ls,l,mid,x);
else
return query(rs,mid+1,r,x);
}
}a;
class BIT{
private:
ll tree[N];
public:
void modify(int x,int k){
if(!x)
return;
for(int i=x;i<=m;i+=lowbit(i))
tree[i]+=k;
return;
}
ll query(int x){
if(!x)
return 0;
ll res=0;
for(int i=x;i;i-=lowbit(i))
res+=tree[i];
return res;
}
}b;
int val[N],t[N];
ll ans[N];
vector<pair<int,int>> ask[N];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>q;
for(int i=1,opt,l,r,k;i<=m;i++){
cin>>opt;
if(opt==1){
cin>>l>>r;
int x=a.query(1,1,n,l),y=a.query(1,1,n,r);
a.modify(1,1,n,l,l,y);
a.modify(1,1,n,r,r,x);
}
else if(opt==2){
cin>>l>>r>>val[i];
a.modify(1,1,n,l,r,i);
}
else{
cin>>k;
t[i]=a.query(1,1,n,k);
}
}
for(int i=1,l,r;i<=q;i++){
cin>>l>>r;
ask[r].push_back(make_pair(l,i));
}
for(int i=1;i<=m;i++){
if(t[i])
b.modify(t[i],val[t[i]]);
for(auto k:ask[i])
ans[k.second]=b.query(i)-b.query(k.first-1);
}
for(int i=1;i<=q;i++)
printf("%lld\n",ans[i]);
return 0;
}

[THUPC 2017] 天天爱射击

法一: 主席树 + 二分答案。对每颗子弹设一个版本,二分版本。\(O(n\log^2n)\)(常数过大无法通过)。 法二: 静态区间第 \(k\) 大。
code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include<cstdio>
#include<vector>
using namespace std;
template<typename T>
void read(T &x){
x=0;
char ch=getchar();
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){
read(x);
read(args...);
return;
}
const int N=2e5+10;
int root[N],tot;
struct segtree{
int sum,ls,rs;
}tree[N*60];
void push_up(int u){
tree[u].sum=tree[tree[u].ls].sum+tree[tree[u].rs].sum;
return;
}
void modify(int &u,int old,int l,int r,int x){
u=++tot;
tree[u]=tree[old];
if(l==r){
tree[u].sum++;
return;
}
int mid=(l+r)/2;
if(x<=mid)
modify(tree[u].ls,tree[old].ls,l,mid,x);
else
modify(tree[u].rs,tree[old].rs,mid+1,r,x);
push_up(u);
return;
}
int query(int u,int old,int l,int r,int k){
if(l==r)
return l;
int mid=(l+r)/2,s=tree[tree[u].ls].sum-tree[tree[old].ls].sum;
if(k<=s)
return query(tree[u].ls,tree[old].ls,l,mid,k);
else
return query(tree[u].rs,tree[old].rs,mid+1,r,k-s);
}
int n,m,ans[N];
struct node{
int x1,x2,s;
}q[N];
vector<int> b[N];
int main(){
read(n,m);
for(int i=1;i<=n;i++)
read(q[i].x1,q[i].x2,q[i].s);
for(int i=1,x;i<=m;i++){
read(x);
b[x].push_back(i);
}
for(int i=1;i<=2e5;i++){
root[i]=root[i-1];
for(int t:b[i])
modify(root[i],root[i],1,m+1,t);
}
for(int i=1;i<=n;i++){
int x1=q[i].x1,x2=q[i].x2,s=q[i].s;
int k=query(root[x2],root[x1-1],1,m+1,s);
ans[k]++;
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}

Legacy

线段树优化建图。

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
template<typename T>
void read(T &x){
x=0;
char ch=getchar();
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){
read(x);
read(args...);
return;
}
typedef long long ll;
const int N=1e5+10;
#define ls (u<<1)
#define rs (u<<1|1)
#define K 4e5
#define inf 4557430888798830399
int n,q,s,a[N],tot_edge,head[N*8];
struct edge{
int to,nxt,w;
}e[N*50];
void add_edge(int u,int v,int w){
e[++tot_edge].to=v;
e[tot_edge].nxt=head[u];
e[tot_edge].w=w;
head[u]=tot_edge;
return;
}
void build(int u,int l,int r){
if(l==r){
a[l]=u;
return;
}
add_edge(u,ls,0),add_edge(u,rs,0);
add_edge(ls+K,u+K,0),add_edge(rs+K,u+K,0);
int mid=(l+r)/2;
build(ls,l,mid);
build(rs,mid+1,r);
return;
}
void modify(int u,int l,int r,int x,int y,int v,int w,bool rev){
if(x<=l&&y>=r){
if(rev)
add_edge(v+K,u,w);
else
add_edge(u+K,v,w);
return;
}
int mid=(l+r)/2;
if(x<=mid)
modify(ls,l,mid,x,y,v,w,rev);
if(y>mid)
modify(rs,mid+1,r,x,y,v,w,rev);
return;
}
priority_queue<pair<ll,int>,vector<pair<ll,int>>,greater<pair<ll,int>>> que;
bool vis[N*8];
ll dis[N*8];
signed main(){
memset(dis,0x3f,sizeof(dis));
read(n,q,s);
build(1,1,n);
for(int i=1;i<=n;i++)
add_edge(a[i],a[i]+K,0),add_edge(a[i]+K,a[i],0);
for(int i=1,opt,x,y,v,w;i<=q;i++){
read(opt);
if(opt==1){
read(x,y,w);
add_edge(a[x]+K,a[y],w);
}
else if(opt==2){
read(v,x,y,w);
modify(1,1,n,x,y,a[v],w,1);
}
else{
read(v,x,y,w);
modify(1,1,n,x,y,a[v],w,0);
}
}
dis[(int)(a[s]+K)]=0;
que.push(make_pair(0,a[s]+K));
while(!que.empty()){
int u=que.top().second;
que.pop();
if(vis[u])
continue;
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to,w=e[i].w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
que.push(make_pair(dis[v],v));
}
}
}
for(int i=1;i<=n;i++){
if(dis[a[i]]==inf)
printf("-1 ");
else
printf("%lld ",dis[a[i]]);
}
return 0;
}

[NOI2018] 归程

使用 Kruskal 重构树,倍增查找,时间复杂度 \(O(n\log n)\)多测一定要记得清 head 数组……

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
template<typename T>
void read(T &x){
bool f=0;
x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
if(f)
x=~x+1;
return;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){
read(x);
read(args...);
return;
}
template<typename T>
T Min(const T &a,const T &b){
return a<b?a:b;
}
const int N=2e5+10,M=4e5+10;
int t;
struct node{
int u,v,a;
bool operator<(const node &x)const{
return a>x.a;
}
}p[M*2];
struct edge{
int to,nxt,w;
}e[M*2];
int tot_edge,head[N];
void add_edge(int u,int v,int w){
e[++tot_edge].to=v;
e[tot_edge].w=w;
e[tot_edge].nxt=head[u];
head[u]=tot_edge;
return;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> que;
int dis[N*2];
bool vis[N];
void dijkstra(){
dis[1]=0;
que.push(make_pair(0,1));
while(!que.empty()){
int u=que.top().second;
que.pop();
if(vis[u])
continue;
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to,w=e[i].w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
que.push(make_pair(dis[v],v));
}
}
}
return;
}
int n,m,q,k,s,fa[M*2],w[M*2],f[M*2][21];
int find(int x){
if(fa[x]==x)
return x;
return fa[x]=find(fa[x]);
}
void kruskal(){
int cnt=n;
sort(p+1,p+1+m);
for(int i=1;i<=n*2;i++)
fa[i]=i;
for(int i=1;i<=m;i++){
int x=find(p[i].u),y=find(p[i].v);
if(x==y)
continue;
fa[x]=fa[y]=++cnt;
w[cnt]=p[i].a;
dis[cnt]=Min(dis[x],dis[y]);
f[x][0]=f[y][0]=cnt;
}
for(int i=1;(1<<i)<=cnt;i++)
for(int j=1;j<=cnt;j++)
f[j][i]=f[f[j][i-1]][i-1];
return;
}
int lastans,v0,p0;
int query(int x,int y){
for(int i=20;i>=0;i--)
if(f[x][i]&&w[f[x][i]]>y)
x=f[x][i];
return dis[x];
}
int main(){
freopen("return.in","r",stdin);
freopen("return.out","w",stdout);
read(t);
while(t--){
tot_edge=lastans=0;
memset(w,0,sizeof(w));
memset(f,0,sizeof(f));
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(head,0,sizeof(head));
read(n,m);
for(int i=1,l;i<=m;i++){
read(p[i].u,p[i].v,l,p[i].a);
add_edge(p[i].u,p[i].v,l);
add_edge(p[i].v,p[i].u,l);
}
dijkstra();
kruskal();
read(q,k,s);
while(q--){
read(v0,p0);
v0=(v0+k*lastans-1)%n+1;
p0=(p0+k*lastans)%(s+1);
lastans=query(v0,p0);
printf("%d\n",lastans);
}
}
return 0;
}

[SCOI2016] 萌萌哒

倍增+并查集。考虑将区间二进制拆分,之后再合并。\(f_{i,j}\) 表示以 \(i\) 为左端点,长度为 \(2^j\) 的区间的所在连通块的左端点。

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include<cstdio>
#include<algorithm>
template<typename T>
void read(T &x){
bool f=0;
x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
if(f)
x=~x+1;
return;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){
read(x);
read(args...);
return;
}
#define mod 1000000007
const int N=1e5+10;
int n,m,f[N][25],l1,r1,l2,r2,ans,maxn;
int find(int x,int k){
if(x==f[x][k])
return x;
return f[x][k]=find(f[x][k],k);
}
void merge(int x,int y,int k){
int a=find(x,k),b=find(y,k);
if(a!=b)
f[a][k]=b;
return;
}
bool flag=1;
int main(){
read(n,m);
maxn=std::__lg(n);
for(int i=1;i<=n;i++)
for(int j=0;j<=maxn;j++)
f[i][j]=i;
while(m--){
read(l1,r1,l2,r2);
for(int j=maxn;j>=0;j--)
if(l1+(1<<j)-1<=r1){
merge(l1,l2,j);
l1+=1<<j;
l2+=1<<j;
}
}
for(int j=maxn;j;j--)
for(int i=1;i+(1<<j)-1<=n;i++){
int x=find(i,j);
merge(i,x,j-1);
merge(i+(1<<(j-1)),x+(1<<(j-1)),j-1);
}
for(int i=1;i<=n;i++)
if(f[i][0]==i){
if(flag){
ans=9;
flag=0;
}
else
ans=(ans*10ll)%mod;
}
printf("%d\n",ans);
return 0;
}

HH 去散步

设边权均为 \(1\),计算邻接矩阵 \(A\)\(k\) 次幂,则 \(A^k_{i,j}\) 表示走 \(k\) 步能连接 \(i\)\(j\) 两点间的路径条数。但这道题要求不走回头路,我们考虑点边互换,记录入边和出边来避免走回头路。

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include<cstdio>
#include<cstring>
template<typename T>
void read(T &x){
bool f=0;
x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
if(f)
x=~x+1;
return;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){
read(x);
read(args...);
return;
}
template<typename T>
void write(T x){
if(x<0){
x=~x+1;
putchar('-');
}
if(x<10)
putchar(x+48);
else{
write(x/10);
putchar(x%10+48);
}
return;
}
#define mod 45989
const int N=310;
int cnt;
struct Matrix{
int x[N][N];
Matrix(){
memset(x,0,sizeof(x));
}
Matrix operator*(const Matrix &a){
Matrix res;
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
for(int k=1;k<=cnt;k++)
res.x[i][j]=(res.x[i][j]+x[i][k]*a.x[k][j]%mod)%mod;
return res;
}
}p;
int n,m,t,a,b,res;
Matrix qpow(Matrix a,int b){
Matrix res;
for(int i=1;i<=cnt;i++)
res.x[i][i]=1;
while(b){
if(b&1)
res=res*a;
a=a*a;
b>>=1;
}
return res;
}
struct edge{
int u,v;
}e[N];
int main(){
read(n,m,t,a,b);
a++,b++;
e[cnt=1].u=0,e[1].v=a;
for(int i=1,u,v;i<=m;i++){
read(u,v);
u++,v++;
e[++cnt].u=u;
e[cnt].v=v;
e[++cnt].u=v;
e[cnt].v=u;
}
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
if(e[i].v==e[j].u&&i!=(j^1)&&i!=j)
p.x[i][j]=1;
p=qpow(p,t);
for(int i=1;i<=cnt;i++)
if(e[i].v==b)
res=(res+p.x[1][i])%mod;
write(res);
putchar('\n');
return 0;
}

[SDOI2015] 寻宝游戏

将关键点按 dfn 排序为 \(a_1,a_2,a_3,\cdots,a_n\),则包含所有关键点的最小生成树的边权和的 \(2\) 倍为 \(dis(a_1,a_2)+dis(a_2,a_3)+\cdots+dis(a_n,a_1)\)。 实现方面,使用一个 set 来查询前驱后继。这题不开 long long 一分没有。

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include<cstdio>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;
template<typename T>
void read(T &x){
bool f=0;
x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
if(f)
x=~x+1;
return;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){
read(x);
read(args...);
return;
}
typedef long long ll;
const int N=1e5+10;
int n,m;
ll ans,d[N];
int dfn[N],son[N],dfncnt,idfn[N],dep[N],siz[N],top[N],fa[N];
struct edge{
int to,w;
};
vector<edge> e[N];
set<int> st;
void dfs1(int u,int f){
siz[u]=1;
fa[u]=f;
dep[u]=dep[f]+1;
for(auto i:e[u]){
int v=i.to,w=i.w;
if(v==f)
continue;
d[v]=d[u]+w;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])
son[u]=v;
}
return;
}
void dfs2(int u,int topf){
top[u]=topf;
dfn[u]=++dfncnt;
idfn[dfncnt]=u;
if(!son[u])
return;
dfs2(son[u],topf);
for(auto i:e[u]){
int v=i.to;
if(v==fa[u]||v==son[u])
continue;
dfs2(v,v);
}
return;
}
int lca(int a,int b){
while(top[a]!=top[b]){
if(dep[top[a]]>dep[top[b]])
a=fa[top[a]];
else
b=fa[top[b]];
}
return dep[a]>dep[b]?b:a;
}
ll dis(int a,int b){
a=idfn[a],b=idfn[b];
return d[a]+d[b]-2*d[lca(a,b)];
}
void add(int t){
if(st.empty()){
ans=0;
st.insert(t);
return;
}
if(st.size()==1){
ans=2*dis(*st.begin(),t);
st.insert(t);
return;
}
st.insert(t);
int x,y;
auto it=st.find(t);
if(it==st.begin())
x=*--st.end();
else
x=*--it;
it=st.find(t);
if(it==--st.end())
y=*st.begin();
else
y=*++it;
ans+=dis(x,t)+dis(t,y)-dis(x,y);
return;
}
void del(int t){
if(st.size()<=2){
st.erase(t);
ans=0;
return;
}
if(st.size()==3){
st.erase(t);
ans=2*dis(*st.begin(),*--st.end());
return;
}
int x,y;
auto it=st.find(t);
if(it==st.begin())
x=*--st.end();
else
x=*--it;
it=st.find(t);
if(it==--st.end())
y=*st.begin();
else
y=*++it;
ans-=dis(x,t)+dis(t,y)-dis(x,y);
st.erase(t);
return;
}
int main(){
read(n,m);
for(int i=1;i<n;i++){
int x,y,z;
read(x,y,z);
e[x].push_back({y,z});
e[y].push_back({x,z});
}
dfs1(1,0);
dfs2(1,1);
while(m--){
int t;
read(t);
t=dfn[t];
if(st.find(t)==st.end())
add(t);
else
del(t);
printf("%lld\n",ans);
}
return 0;
}