2023NOIP A层联测32-sakuya

红魔馆的内部形态是一棵树,总共有 n n n 个房间,其中它们由 n − 1 n-1 n1 个走廊连接。

每条走廊都有一个难走程度 w w w

每一天,十六夜咲夜都会打扫房间。

她在打扫房间的时候会使用能力,暂停时间。所以打扫一个房间不要任何时间。然而进过一个走廊是要花费时间的,所话花费的时间就是这个走廊的难走程度。

现在给出 m m m 个特殊房间,把这些房间记为集合 A A A 。现在十六夜咲夜想随机的从一个房间开始打扫,然后随机进入另一个 A A A 中没有被打扫的房间去打扫它,直到把这 m m m 个房间都打扫干净。

现在,她想知道她把这些房间打扫干净的期望时间对 998244353 998244353 998244353 取模的值。

换句话说,设 d ( x , y ) d(x,y) d(x,y) 表示从树上点 x x x y y y 的边权和。

A A A 随机打乱得到序列 a a a

∑ i = 2 m d ( a i − 1 , a i )         m o d   998244353 \sum\limits_{i=2}^m d(a_{i-1},a_i) \ \ \ \ \ \bmod 998244353 i=2md(ai1,ai)     mod998244353 。的期望值

但是每天早上,帕秋莉会在某一个房间 x x x 使用魔法,魔法有一个强度 k k k 。这会使得所有与 x x x 相连的走廊的难走程度增加 k k k。(十六夜咲夜:谢谢你啊。)

而你需要回答每天使用魔法后的期望值。

1 ≤ n ≤ 5 × 1 0 5 , m ≤ n , 1 ≤ q ≤ 5 × 1 0 5 , 1 ≤ w , k ≤ 1 0 9 1 \le n\le 5\times 10^5,m\le n,1 \le q \le5\times 10^5,1 \le w,k \le 10^9 1n5×105,mn,1q5×105,1w,k109


先快速求出期望。先把期望转换为求出方案的总和除以 m ! m! m!,选定 a i , a j ( i ≠ j ) a_i,a_j(i\not=j) ai,aj(i=j) 作为 a a a 连续的两个元素,然后在所有方案中, d ( a i , a j ) d(a_i,a_j) d(ai,aj) 会出现 ( m − 1 ) ! (m-1)! (m1)! 次,于是可以 O ( m 2 log ⁡ n ) O(m^2\log n) O(m2logn) 求出期望( log ⁡ \log log 是求两点 lca)。

考虑继续优化。令节点 1 1 1 为根, s z u sz_u szu 表示子树 u u u 所含有特殊节点的个数, f a u fa_u fau u u u 的父亲。对于一条边 ( u , f a u , w ) (u,fa_u,w) (u,fau,w),会有 s z u ⋅ ( m − s z u ) sz_u\cdot(m-sz_u) szu(mszu) 条路径经过这条边,贡献就是 s z u ⋅ ( m − s z u ) ⋅ w sz_u\cdot(m-sz_u)\cdot w szu(mszu)w。于是我们可以 O ( n ) O(n) O(n) 求出期望。

对于每次修改,如果直接暴力修改边权再求期望,复杂度是 O ( q n ) O(qn) O(qn) 的,不行。

我们先求出一开始的期望,然后每次修改发现答案都会增加 k ( s z u ( m − s z u + ∑ v ∈ s o n u s z v ( m − s z v ) ) ) k\left(sz_u(m-sz_u+\sum\limits_{v\in son_u}sz_v(m-sz_v))\right) k(szu(mszu+vsonuszv(mszv))),所以对每个点 u u u 预处理出式子的后半部分 b u b_u bu,每次询问就把答案加上即可。

时间复杂度 O ( n + q ) O(n+q) O(n+q)

#include
using namespace std;
#define ll long long
const int N=5e5+10;
constexpr ll mod=998244353;
int n,m,q,a[N];
int head[N],nxt[N<<1],to[N<<1],w[N<<1],cnt,SZ[N];
ll ans,b[N];
void add(int u,int v,int W)
{
    to[++cnt]=v;
    w[cnt]=W;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
ll ksm(ll a,ll b)
{
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}
void dfs1(int u,int fa)
{
    for(int i=head[u];i;i=nxt[i]){
        if(to[i]!=fa){
            dfs1(to[i],u);
            SZ[u]+=SZ[to[i]];
            ans=(ans+2ll*w[i]*SZ[to[i]]%mod*(m-SZ[to[i]]))%mod;
            b[u]=(b[u]+2ll*SZ[to[i]]*(m-SZ[to[i]]))%mod;
        }
    }
    b[u]=(b[u]+2ll*SZ[u]*(m-SZ[u]))%mod;
}
int main()
{
    freopen("sakuya.in","r",stdin);
    freopen("sakuya.out","w",stdout);
    cin.tie(0)->sync_with_stdio(0);
    cin>>n>>m;
    for(int i=1,x,y,w;i<n;i++){
        cin>>x>>y>>w;
        add(x,y,w),add(y,x,w);
    }
    for(int i=1;i<=m;i++) cin>>a[i],SZ[a[i]]=1;
    dfs1(1,0);
    cin>>q;
    ll invm=ksm(m,mod-2);
    for(int i=1,x,k;i<=q;i++){
        cin>>x>>k;
        ans=(ans+k*b[x])%mod;
        cout<<ans*invm%mod<<"\n";
    }
}

你可能感兴趣的:(算法)