GYM 100962F Problem F. Frank Sinatra(树上莫队+分块)

题目链接

Problem F. Frank Sinatra

分析

这题和前面那个题唯一不一样的地方是,这题访问的是边上的,因此可以将边上的值算做入边顶点的值,这样就  u,v 对应的区间就是  [dfl[u]+1,dfl[v]] , 这样开个桶记录访问到的数就行了.

code

#include <bits/stdc++.h>
using namespace std;
#define ms(x,v) (memset((x),(v),sizeof(x)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long LL;
typedef pair<int,int > Pair;
const int maxn = 1e5+10;
int n,m;
const int S = 320;
struct Query{
    int id,l,r,backet;
    bool operator<(const Query & o)const{
        return backet==o.backet?(backet&1?r>o.r : r <o.r) : l<o.l;
    }
} q[maxn];
std::vector<Pair> G[maxn];
int dfl[maxn],dfr[maxn],dfn[maxn<<1],val[maxn],dft=0;
int cnt[maxn],vis[maxn],sum[maxn],ret[maxn];
void dfs(int x) {
    dfl[x] = ++dft;dfn[dft] = x;
    for(auto v : G[x])
        if(!dfl[v.fi])val[v.fi] =v.se, dfs(v.fi);
    dfr[x] = ++dft;dfn[dft] = x;
}

inline void move(int node) {
    if(val[node]>n)return;
    if(vis[node] && --cnt[val[node]]==0)--sum[val[node]/S];
    else if(!vis[node] && ++cnt[val[node]]==1)++sum[val[node]/S];
    vis[node] ^=1;
}
inline void mo(/* arguments */) {
    int l=  q[0].l,r = q[0].l-1,L,R;
    for(int i=0 ;i<m ; ++i){
        L = q[i].l,R = q[i].r;
        while (l > L)move(dfn[--l]);
        while (r < R)move(dfn[++r]);
        while (l < L)move(dfn[l++]);
        while (r > R)move(dfn[r--]);
        int j;
        for( j=0;sum[j]==S ; ++j);
        for(j*=S ; cnt[j]; ++j);
        ret[q[i].id]  = j;
    }
}
int main(int argc, char const *argv[]) {
    scanf("%d%d",&n,&m );
    for(int i=1 ; i<n ; ++i){
        int u,v,c;
        scanf("%d%d%d",&u,&v,&c );
        G[u].pb(mp(v,c));G[v].pb(mp(u,c));
    }
    dfs(1);
    for(int i=0 ; i<m ; ++i){
        int u,v;
        scanf("%d%d",&u,&v );
        if(dfl[u]> dfl[v])swap(u,v);
        q[i].id = i;
        q[i].l= dfl[u]+1;q[i].r = dfl[v];q[i].backet = q[i].l/S;
    }
    sort(q,q+m);
    mo();
    for(int i=0 ; i<m ; ++i)printf("%d\n",ret[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值