2020.1.20考试总结

题目链接: https://pan.baidu.com/s/13xPeJUH0AGnZPPyVTNaVig 提取码: s59u

T1传销组织

给定一些要求比如 , 要有 <a, b> 联通 , <a,c> 不连通;

这题一眼bitset维护 , 但是算吧算吧发现空间开不下 , 于是题解上就出现了一种神奇的解法 。

牺牲时间 , 每次开一个指定大小的bitset 的第二维 , 每次只处理一段的询问,。。。。

#include<iostream>
#include<cstdio>
#include<bitset>
#include<queue>
using namespace std;
const int N = 1e5+10;
inline int read()
{
    register int x = 0; register char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
    return x;
}
int n , m , Q , cnt , top , num , tot , Cnt;
int head[N] , Head[N] , dfn[N] , low[N] , vis[N] , sta[N] , a[N] , b[N] , col[N] , du[N] , d[N];
bitset<20020> bit[N];
struct edge{int u , v , nex; } e[N] , E[N];
inline void add(int u , int v) { e[++cnt].u = u; e[cnt].v = v; e[cnt].nex = head[u]; head[u] = cnt; return ; }
inline void addc(int u , int v) { E[++Cnt].v = v; E[Cnt].nex = Head[u]; Head[u] = Cnt; return ;}

void Tarjan(int x)
{
    dfn[x] = low[x] = ++tot; vis[x] = 1; sta[++top] = x;
    for(int i = head[x] , v; i ; i = e[i].nex)
    {
        v = e[i].v; 
        if(!dfn[v]) Tarjan(v) , low[x] = min(low[x] , low[v]);
        else if(vis[v]) low[x] = min(low[x] , dfn[v]); 
    }
    if(low[x] == dfn[x])
    {
        int t = 0; ++num;
        do
        {
            t = sta[top--]; vis[t] = 0; col[t] = num;
        }while(t != x);
    }
    return ;
}

queue<int> q;
bool solve(int l , int r)
{
    for(int i = 1 ; i <= num ; ++i) d[i] = du[i] , bit[i].reset();
    for(int i = 1 ; i <= num ; ++i) if(d[i] == 0) q.push(i);
    while(q.size())
    {
        int x = q.front(); q.pop();
        if(l <= x && x <= r) bit[x][x-l] = 1;
        for(int i = Head[x] , v; i ; i = E[i].nex)
        {
            v = E[i].v; bit[v] |= bit[x];
            if(--d[v] == 0) q.push(v);
        }
    }
    for(int i = 1 ; i <= Q ; ++i)
        if(l <= b[i] && b[i] <= r && bit[a[i]][b[i]-l]) return true;
    return false;
}

int main()
{
    freopen("gplt.in" , "r" , stdin);
    freopen("gplt.out" , "w" , stdout);
    n = read(); m = read();
    for(int i = 1 , a , b; i <= m ; ++i) a = read() , b = read() , add(a , b);
    for(int i = 1 ; i <= n ; ++i) if(!dfn[i]) Tarjan(i);
    for(int i = 1 ; i <= n ; ++i)
        for(int j = head[i] , v; j ; j = e[j].nex)
        {
            v = e[j].v; if(col[i] == col[v]) continue;
            addc(col[v] , col[i]); du[col[i]]++;
        }
    Q = read();
    for(int i = 1 , x , y; i <= Q ; ++i)
    {
        x = read(); y = read();
        a[i] = col[x]; b[i] = col[y];
    }
    for(int l = 1 , r; l <= num ; l = r + 1)
    {
        r = min(num , l + 20000);
        if(solve(l , r)) { puts("NO"); fclose(stdin); fclose(stdout); return 0; }
    }
    puts("YES");
    for(int i = 1 ; i <= m ; ++i) printf("%d %d\n" , e[i].u , e[i].v);
    fclose(stdin); fclose(stdout); return 0;
}
/*
3
2
1 2
2 3
1
1 3
*/

T3 树

\((\sum a_i)^k\) 拆开发现就是在一堆 a 中可重的选择k个数乘在一次 , 所有方案的和。

\(f(i , j)\) 表示 i 这个以节点为根的子树内选择出 j 个点的期望值。

考虑转移
\[ \large f[i][j] = (1 - p) *f[i][j] + p * \sum_{x=0}^j(f[son][x] * f[i][j-x] * C(j , x)) \]
其中 p 是 i 连向son的这条边出现的概率。

意思就是分类讨论,如果这条边没有出现 , 那他就是之前的儿子的贡献 , 否则就可以考虑他的儿子中选择了几个节点 , 以及那些个节点 , 也就是后面的一坨东西。

后面的显然是一个卷积的形式 (把组合数拆了),用NTT优化一下转移过程即可。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1100 , mod = 998244353 , G = 3 , Ginv = (mod + 1) / 3;
#define int long long
inline int read()
{
    register int x = 0; register char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
    return x;
}
int n , k , cnt , ans;
int head[N] , a[N] , f[N][N] , A[N] , B[N] , C[N] , r[N] , fac[N] , inv[N];
struct edge{ int v , nex , p; } e[N<<1];
inline void add(int u , int v , int p) { e[++cnt].v = v; e[cnt].nex = head[u]; e[cnt].p = p; head[u] = cnt; return ; }
inline int ksm(int a , int k) { int ans = 1; for( ; k ; k >>= 1 , a = a * a % mod) if(k & 1) ans = ans * a % mod; return ans; }
 
void Init()
{
    fac[0] = inv[0] = 1;
    for(int i = 1 ; i <= 1000 ; ++i) fac[i] = fac[i-1] * i % mod;
    inv[1000] = ksm(fac[1000] , mod - 2);
    for(int i = 999 ; i >= 1 ; --i) inv[i] = inv[i+1] * (i+1) % mod;
    return ;
}
 
void NTT(int *A , int lim , int opt)
{
    for(int i = 0 ; i < lim ; ++i) if(r[i] > i) swap(A[i] , A[r[i]]);
    for(int mid = 1 ; mid < lim ; mid <<= 1)
    {
        int len = mid << 1 , Wn = ksm(opt == 1 ? G : Ginv , (mod - 1) / len);
        for(int j = 0 ; j < lim ; j += len)
        {
            int w = 1;
            for(int k = 0 ; k < mid ; ++k , w = w * Wn % mod)
            {
                int x = A[j + k] , y = A[j + k + mid] * w % mod;
                A[j + k] = (x + y) % mod;
                A[j + k + mid] = (x - y) % mod;
            }
        }
    }
    if(opt == 1) return ;
    int inv = ksm(lim , mod - 2);
    for(int i = 0 ; i < lim ; ++i) A[i] = A[i] * inv % mod;
    return; 
}
 
void MUL(int *a , int *b)
{
    memset(A , 0 , sizeof A); memset(B , 0 , sizeof B); memset(C , 0 , sizeof C);
    int lim = 1 , l = 0;
    while(lim < (k * 2)) lim <<= 1 , l++;
    for(int i = 0 ; i < lim ; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
    for(int i = 0 ; i < k ; ++i) A[i] = a[i] * inv[i] % mod , B[i] = b[i] * inv[i] % mod;
    NTT(A , lim , 1); NTT(B , lim , 1);
    for(int i = 0 ; i < lim ; ++i) C[i] = A[i] * B[i] % mod;
    NTT(C , lim , -1); return ;
}
 
void dfs(int x , int fa , int fp)
{
    f[x][0] = 1;
    for(int i = 1 ; i < k ; ++i) f[x][i] = f[x][i-1] * a[x] % mod;
    for(int i = head[x] , v; i ; i = e[i].nex)
    {
        v = e[i].v; if(v == fa) continue; int p = e[i].p;
        dfs(v , x , p); MUL(f[x] , f[v]);
        for(int j = 0 ; j < k ; ++j)
        {
            f[x][j] = (1 - p + mod) % mod * f[x][j] % mod;
            f[x][j] = (f[x][j] + p % mod * fac[j] % mod * C[j] % mod) % mod;
        }
    }
    ans = (ans + f[x][k-1] * (1 - fp + mod) % mod) % mod;
    return ;
}
 
signed main()
{
    freopen("tree.in" , "r" , stdin);
    freopen("tree.out" , "w" , stdout);
    n = read(); k = read(); Init(); k++;
    for(int i = 1 ; i <= n ; ++i) a[i] = read();
    for(int i = 1 ; i < n  ; ++i)
    {
        int u = read() , v = read() , a = read() , b = read();
        a = a * ksm(b , mod - 2) % mod;
        add(u , v , a); add(v , u , a);
    }
    dfs(1 , 0 , 0); printf("%lld\n", ans);
    return 0;
}
/*
4 1
1 2 3 2
1 2 1 2
2 3 2 3
2 4 1 3
*/

猜你喜欢

转载自www.cnblogs.com/R-Q-R-Q/p/12220547.html