1. 程式人生 > 其它 >AtCoder ABC 222E

AtCoder ABC 222E

原題連結

分析

首先,我們可通過DFS處理出來,在沿著a陣列給的順序走時,每條邊都經過了多少次。我們假設第i條邊被經過的次數為\(c_i\)次,則問題可以轉化為,從\(c_1,c_2...c_{n-1}\)中,分成兩部分,使得一部分構成R,一部分構成B,從而使得R-B=K?我們設S=\(c_1+c_2+...+c_{n-1}\),而又因為R+B=S,因此我們可以解出

\[R=\frac{S+K}{2} \]

因此,問題在此進行傳化,從\(c_1,c_2...c_{n-1}\)中,挑選出一部分數,使得其和為\(\frac{S+K}{2}\)?

問題到這裡,已經很明顯了,01揹包計數問題

但有一些細節需要處理,當S+K是奇數的時候,是無解的,同時S+K是負數也無解。

接下來就是01計數揹包

\[f[i][j]=從c_1,c_2...,c_i中選擇和為j的方案數 \]

AcCode

#include <bits/stdc++.h>
#define x first    
#define y second    
#define debug(x) cout<<#x" ----> "<<x<<endl
using namespace std;
 
const int N = 1010,M=N*2,INF = 0x3f3f3f3f,mod=998244353;
const double eps = 1e-10;
const double pi = acos(-1.0);
typedef pair<int, int> PII ;
typedef pair<double,double> PDD;
typedef long long LL;
int h[N],e[M],ne[M],idx;
int cnt[N];
int w[N];
int dp[100001];
int n,m,k;

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

bool dfs(int u,int fa,int end)
{
    if(u==end) return 1;
    for(int i=h[u];~i;i=ne[i])
    {
        int j = e[i];
        if(j==fa) continue;
        if(dfs(j,u,end))
        {
            cnt[i/2]++;
            return 1;
        }
    }
    return 0;
}

void solve() {
    memset(h,-1,sizeof h);
    idx=0;
    cin>>n>>m>>k;
    for(int i=0;i<m;i++) cin>>w[i];
    for(int i=0;i<n-1;i++) 
    {
        int u,v;
        cin>>u>>v;
        add(u,v),add(v,u);
    }

    for(int i=0;i<m-1;i++) dfs(w[i],-1,w[i+1]);

    int s = 0;
    for(int i=0;i<n-1;i++) s+=cnt[i];
    if((s+k)%2||s+k<0)
    {
        cout<<0<<endl;
        return ;
    }

    dp[0]=1;
	for(int i=0;i<n-1;i++)
        for(int x=100000;x>=cnt[i];x--)
            dp[x]=(LL)(dp[x]+dp[x-cnt[i]])%mod;
	cout << dp[(s+k)/2]<<endl;
}
 
int main() 
{
    int t=1;
 
    while(t -- ) {
        solve();
    }
 
    return 0;
}