省赛真题-砍树(树剖写法)

187 阅读2分钟

题目描述

给定一棵由 n 个结点组成的树以及 m 个不重复的无序数对 (a1, b1), (a2, b2), . . . , (am, bm),其中 ai 互不相同,bi 互不相同,ai ≠ bj(1 ≤ i, j ≤ m)。 小明想知道是否能够选择一条树上的边砍断,使得对于每个 (ai , bi) 满足 ai和 bi 不连通,如果可以则输出应该断掉的边的编号(编号按输入顺序从 1 开始),否则输出 -1.

输入格式

一行一个整数,表示答案,如有多个答案,输出编号最大的一个。

输出

行一个整数,表示答案,如有多个答案,输出编号最大的一个。

思路

我们考虑给u->v经过得边边权加一代表这条边我们经过一次同理可得如果边权是m代表我们经过m次也就是如果断掉这条边经过这条边的两个点将无法联通

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define int long long
#define endl '\n'
#define pb push_back
#define NO cout << "NO" << endl;
#define YES cout << "YES" << endl;
#define fi first
#define se second
#define all(x) (x).begin(),(x).end()
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<VI> VII;
ll MOD = 998244353;
ll powmod(ll a,ll b) {ll res=1;a%=MOD; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%MOD;a=a*a%MOD;}return res;}
const int N = 2e5 + 10;
int w[N];
int n,m;
int son[N],id[N],fa[N],cnt,dep[N],siz[N],top[N],wt[N]; 
vector<int> v[N];
int f[N << 2],Lazy[N << 2];
void Built(int k,int l,int r) {
    if( l == r) {
        f[k] = wt[l];
        f[k] %= MOD;
        // cout << f[k] << endl;
        return;
    }
    int m = l + r >> 1;
    Built(k<<1,l,m);
    Built(k<<1|1,m+1,r);
    f[k] = f[k << 1] + f[k << 1|1];
    f[k] %= MOD;
}
void update(int k,int l,int r,int L,int R,int s)
{
    if( l == L&& r == R)
    {
        Lazy[k] += s;
        return;
    }
    if(Lazy[k]) {
        Lazy[k <<1] += Lazy[k];
        Lazy[k << 1|1] += Lazy[k];
        Lazy[k] = 0;
    }
    int m = (l + r) >> 1;
    if(R <= m) update(k << 1,l,m,L,R,s);
    else
    {
       if(L > m) update(k << 1|1,m+1,r,L,R,s);
       else update(k << 1,l,m,L,m,s),update(k <<1|1,m+1,r,m+1,R,s);
    }
       f[k] = f[k << 1] + Lazy[k << 1] *(m - l + 1) + f[k << 1 | 1] + Lazy[k <<1|1] * (r - m);
       f[k] %= MOD;
}
int calc(int k,int l,int r,int x,int y)
{
    if(l == x && r == y){
        return (f[k] + Lazy[k] * (r - l + 1)) % MOD;
    }
    if(Lazy[k]) Lazy[k << 1] += Lazy[k], Lazy[k << 1|1] += Lazy[k],Lazy[k] = 0;
    int res = 0;
    int m = (l + r) >> 1;
    if(y <= m) res = calc(k << 1,l,m,x,y);
    else {
        if(x > m) res = calc(k<<1|1,m+1,r,x,y);
        else res = calc(k<<1,l,m,x,m) + calc(k<<1|1,m+1,r,m+1,y);
    }
    f[k] = f[k << 1] + Lazy[k << 1]  *(m - l + 1) + f[k << 1 | 1] + Lazy[k <<1|1] * (r - m);
    return res % MOD;
}
void dfs1(int x,int f,int deep) { // 找到每个结点的子节点数量和重儿子
   dep[x] = deep;
   fa[x] = f;
   siz[x] = 1;
   int maxson = -1;
   for(auto u : v[x]) {
     if(u == f) continue;
     dfs1(u,x,deep + 1);
     siz[x] += siz[u];
     if(siz[u] > maxson) {
           son[x] = u;
           maxson = siz[u];
     }
   }
}
void dfs2(int x,int topf) {
    id[x] = ++cnt;
    wt[cnt] = w[x];
    top[x] = topf;
    if(!son[x]) return;
    dfs2(son[x],topf);
    for(auto u : v[x]) {
        if(u == fa[x] || u == son[x]) continue;
        dfs2(u,u);
    }
}
int qRange(int x,int y){ // x -> y 区间求和
    int ans=0;
    while(top[x] != top[y]){//当两个点不在同一条链上 
        if(dep[top[x]] < dep[top[y]])swap(x,y);//把x点改为所在链顶端的深度更深的那个点
        ans += calc(1,1,n,id[top[x]],id[x]);//ans加上x点到x所在链顶端 这一段区间的点权和
        ans %= MOD;//按题意取模 
        x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点
    }
    //直到两个点处于一条链上
    if(dep[x] > dep[y])swap(x,y);//把x点深度更深的那个点
    int res = 0;
    ans += calc(1,1,n,id[x],id[y]);//这时再加上此时两个点的区间和即可
    return ans % MOD;
}
void updRange(int x,int y,int k){// 区间修改
    k %= MOD;
    while(top[x] != top[y]){
        if(dep[top[x]] < dep[top[y]]) swap(x,y);
        update(1,1,n,id[top[x]],id[x],k);
        x = fa[top[x]];
    }
    if(dep[x] > dep[y])swap(x,y);
    update(1,1,n,id[x],id[y],k);
}
int qSon(int x){ //单点查询
    int res = 0;
    return calc(1,1,n,id[x],id[x] + siz[x]-1);//子树区间右端点为id[x]+siz[x]-1 
     // return res;
}
void updSon(int x,int k) { // 更新子树
    update(1,1,n,id[x],id[x] + siz[x]-1,k);//
    // update(1,1,n,id[x],id[x],k); // 更新单个节点

}
void solve()
{
    cin >> n >> m;
    vector<PII> a(n + 1);
    rep (i, 1, n - 1) {
        int x, y; cin >> x >> y;
        a[i] = {x, y};
        v[x].pb(y);
        v[y].pb(x);
    }
    int r = 1;
    dfs1(r,0,1), dfs2(r,r);
    Built(1, 1, n);
    rep (i, 1, m) {
        int x, y; cin >> x >> y;
        updRange(x, y, 1);
    }
    VI s(n + 1);
    rep (i, 1, n) s[i] = qRange(i, i);
    // rep (i, 1, n) cout << s[i] << " \n"[i == n];
    per (i, 1, n - 1) {
        auto [x, y] = a[i];
        if (s[x] == m && s[y] == m) {
            cout << i << endl;
            return;
        }
    }
    cout << -1 << endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    // int T;cin >> T;
    // while ( T -- )
    solve();
    return 0;
}