【loj6496「雅礼集训 2018 Day1」仙人掌 】

要期末考试了,感觉这一学期就这样水过去了,自己还是这么地菜,,,emmmm.....偶然去了趟晚自习,那笑容真甜owo

题目描述

给出一张$n$个点$m$条边的无向连通图,其中每条边至多属于一个简单环,保证没有自环,可能有重边。你需要为其中每条边定向,其中第$i$个点的出度不能超过$a_i$求方案数。

输入格式

第一行包括两个正整数$n$,$m$接下来 $m$行,每行两个正整数,表示有一条边连接这两个点。 最后一行$n$个正整数,其中第 $i$ 个表示 $a_{i}$

输出格式

输出一个非负整数,表示答案对 $998244353$取模后的结果。

样例

样例输入 1

3 4 1 2 2 1 2 3 3 2 1 2 3

样例输出 1

7 对于全部数据 $1≤ai≤n≤10^5,1≤m≤2×10^5$ 嗯,少有的一道题目的算法真的和题目名字般配的一道题。 根据hdhd大神的描述,他似乎直接秒了,并且觉得很水,,一定是我太菜了,只想到了树的情况,怎么都没想好环的处理方式。果然仙人掌还是没有学好呢。 题意很显然。对于树的情况也比较显然f[x][0/1]表示向父亲边是否有出度,最后答案f[1][0] $f[x][k] = \sum\limits_{k_{1}+k_{2}+...k_{v}<=k}f[x][k_{v}] (v\in x_{son}) $ 分治ntt即可。 那么环怎么办? 我们给dp改为f[x][0/1/2]为了处理可能在环上向两边连边的情况。 假设我们顺时针枚举,我们枚举换上第一条边选了没有,g[x][0/1]表示在环上往逆时针一条边是否有出度。之后直接转移就可以了。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<vector>
using namespace std;
const int maxn = 4e5+5;
const int mod = 998244353;
typedef vector<int> ve;
int add(int x,int y) { x+=y; return x>=mod?x-mod:x; }
int sub(int x,int y) { x-=y; return x<0?x+mod:x; }
int mul(int x,int y) { return 1ll*x*y%mod; }
int ksm(int a,int b) {
    int ans = 1;
    for(;b;b>>=1,a=mul(a,a))
        if(b&1) ans = mul(ans,a);
    return ans;
}
int n,m,tot;
struct edge{
    int en[maxn],nt[maxn],la[maxn],owo;
    void adg(int x,int y) {
    en[++owo]=y; nt[owo]=la[x]; la[x]=owo; 
    }
}V,G;
int low[maxn],dfn[maxn],dfx;

int sta[maxn],top;
void tarjan(int x,int ba) {
    dfn[x] = low[x] = ++dfx;
    sta[++top] = x;
    for(int it=V.la[x];it;it=V.nt[it]) {
        int y = V.en[it];
        if( (it^1)==ba) continue;
        if(!dfn[y]) {
            tarjan(y,it);
            low[x] = min(low[x],low[y]);
            if(low[y]>dfn[x]) G.adg(x,y),top--; 
            else if(low[y]==dfn[x]) {
                ++tot; G.adg(x,tot);
                int o;
                do{
                    o = sta[top--];
                    G.adg(tot,o);
                }while(o!=y);
            }
        } else low[x] = min(low[x],dfn[y]);
    }
}
int A[maxn];
int f[maxn][3];
int g[maxn][2];
int ta[maxn<<1],tb[maxn<<1];
void ntt(int *a,int s,int dft){
    for(int i=0,j=0;i<s;i++) {
        if(i<j) swap(a[i],a[j]);
        for(int k=(s>>1);(j^=k)<k;k>>=1);
    }
    for(int st=1;st<s;st<<=1) {
        int dwg; dwg= ( dft==1 ? ksm(3,(mod-1)/(st<<1)) : ksm(3,(mod-1)-(mod-1)/(st<<1) ) );
        for(int i=0;i<s;i+=(st<<1)) {
            int ng = 1;
            for(int j=i;j<i+st;j++) {
                int x = a[j]; int y = mul(a[j+st],ng);
                ng = mul(dwg,ng);
                a[j] = add(x,y); a[j+st] = sub(x,y);
            }
        }
    }
    if(dft==1) return;
    int invs = ksm(s,mod-2);
    for(int i=0;i<s;i++) a[i] = mul(a[i],invs);
}
int tt;
ve PL[maxn];
void MUL(ve&o,ve&x,ve&y) {
    int OO = x.size() + y.size() - 2;
    int len = 1;
    for(;len<=OO;len<<=1);
    fill(ta,ta+len,0);
    fill(tb,tb+len,0);
    for(int i=x.size()-1;i>=0;i--) ta[i] = x[i];
    for(int i=y.size()-1;i>=0;i--) tb[i] = y[i];
    ntt(ta,len,1); ntt(tb,len,1);
    for(int i=0;i<len;i++) ta[i] = mul(ta[i],tb[i]);
    ntt(ta,len,-1);
    o.resize(OO+1);
    for(int i=0;i<=OO;i++) o[i] = ta[i];
    y.clear();
}
void EZ(int l,int r) {
    if(l==r) return;
    int mid = (l+r)>>1;
    EZ(l,mid); EZ(mid+1,r);
    MUL(PL[l],PL[l],PL[mid+1]);
    return;
}
void dfs(int x) {
    for(int it=G.la[x];it;it=G.nt[it]) {
        int y = G.en[it];
        dfs(y);
    }
    if(x<=n) {
        if(!G.la[x]) {
            f[x][0] = 1;
            if(A[x]>=1) f[x][1] = 1;
            if(A[x]>=2) f[x][2] = 1;
            return;
        }
        tt = 0;
        for(int it=G.la[x];it;it=G.nt[it]) {
            int y = G.en[it];
            ++tt; if(y>n) {
                PL[tt].resize(3); PL[tt][2] = f[y][0]; PL[tt][1] = f[y][1]; PL[tt][0] = f[y][2];
            } else {
                PL[tt].resize(2); PL[tt][1] = f[y][0]; PL[tt][0] = f[y][1];
            }
        }
        EZ(1,tt);
        PL[1].resize(A[x]+1);
        for(int i=1;i<=A[x];i++) PL[1][i] = add(PL[1][i],PL[1][i-1]);
        f[x][0] = PL[1][A[x]];
        if(A[x]>=1) f[x][1] = PL[1][A[x]-1];
        if(A[x]>=2) f[x][2] = PL[1][A[x]-2];
        return;
    } else {
        for(int mj=0;mj<=1;mj++) {
            int oo = 1;
            g[oo][mj] = 1; g[oo][mj^1] = 0;
            for(int it=G.la[x];it;it=G.nt[it]) {
                ++oo; int y = G.en[it];
                g[oo][0] = add( mul(g[oo-1][0],f[y][1] ) , mul(g[oo-1][1],f[y][2]) );
                g[oo][1] = add( mul(g[oo-1][0],f[y][0]) , mul(g[oo-1][1],f[y][1] ) );
            }
            f[x][mj+1] = add(f[x][mj+1],g[oo][0]);
            f[x][mj] = add(f[x][mj],g[oo][1]);
        }
        return;
    }
}
int main() {
//  freopen("ccc.in","r",stdin);
    V.owo = 1;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) {
        int x,y; scanf("%d%d",&x,&y);
        V.adg(x,y); V.adg(y,x);
    }
    for(int i=1;i<=n;i++) scanf("%d",&A[i]);
    tot = n; tarjan(1,0); dfs(1);
    //cerr<<f[5][2]<<endl;
    printf("%d",f[1][0]);
}

posted @ 2019-01-21 01:03  Newuser233  阅读(10)  评论(0)    收藏  举报