五一集训题合集

A Simple Task

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

code
#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
#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
#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
#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
#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
#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
#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
#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
#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;
}
posted @ 2025-05-02 19:16  headless_piston  阅读(17)  评论(0)    收藏  举报