USACO 2019 February Contest Platinum T2: Moorio Kart

题目大意

Bessie和Farmer John喜欢山羊卡丁车比赛。这个比赛非常类似于其他人喜欢的卡丁车比赛,除了卡丁车是由山羊拉动,以及赛道是由农田组成。农田由 N 个草地和 M 条道路组成,每条道路都连接着两个草地。

定义农场是两个或更多草地的一个集合,同一农场中的每个草地都可以沿着一系列唯一的道路到达农场中其他任意一个草地。

整个农田可能由多个农场组成,假设图中有 K 个农场。Bessie希望通过添加长度为 X 的 K 条道路,连接所有 K 个农场来制作山羊卡丁车赛道。每个农场只应访问一次,并且每个农场内必须至少穿过一条道路。

为了让选手们对赛道更有兴趣,赛道的长度至少应该为 Y 。Bessie希望知道所有这些有趣赛道的赛道长度总和。如果一个赛道中有两个农场直接相连,但另外一个赛道中这两个农场没有直接相连的话,这两个赛道就是不同的。

题目分析

因为数据范围很小,可以直接用 dfs 将每棵小树内的所有路径(长度数,长度和)都统计出来。然后就变成了问给你几个集合,从这些集合中每个集合选择一个数,问和 ge YY 的方案和。这是一个简单背包问题。求方案和可以顺便求出。

具体来说,设 dp[i][0/1]表示 和为 i 的方案数/方案和, g[i][0/1] 表示当前小树内长度为 ii 的路径数/长度和,则存在转移:

  dp[i+j][0dp[i][0∗ g[j][0]

  dp[i+j][1← dp[i][0∗ g[j][1dp[i][1∗ g[j][0]

dfs后dp即可

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int MAXN=2510+10;
 5 const ll Mod=1e9+7; 
 6 
 7 struct Node{
 8     int to,nxt,dis;
 9 }e[MAXN<<1];
10 int cnt,head[MAXN];
11 inline void add_edge(int u,int v,int d){
12     e[++cnt].to=v;e[cnt].dis=d;e[cnt].nxt=head[u];head[u]=cnt;
13 }
14 
15 int n,m,X,Y,tcnt;
16 int f[MAXN],bel[MAXN];
17 
18 ll ans;
19 ll dp[MAXN][2],g[MAXN][2];
20 ll tot[MAXN][MAXN],sum[MAXN][MAXN];
21 inline int Find(int x){
22     return f[x]==x?x:f[x]=Find(f[x]);
23 }
24 
25 inline void dfs1(int x,int fath,int id){
26     bel[x]=id;
27     for(int i=head[x],y;i;i=e[i].nxt){
28         y=e[i].to;
29         if(y!=fath)
30             dfs1(y,x,id);
31     }
32 }
33 inline void dfs(int x,int fath,int l){
34     if(fath){  //求出每个子树的方案和/方案数
35         sum[bel[x]][min(l,Y)]=(sum[bel[x]][min(l,Y)]+(1ll*l))%Mod;
36         ++tot[bel[x]][min(l,Y)];
37     }
38     for(int i=head[x],y;i;i=e[i].nxt){
39         y=e[i].to;
40         if(y!=fath)
41             dfs(y,x,l+e[i].dis);
42     }
43 }
44 int main(){
45     scanf("%d%d%d%d",&n,&m,&X,&Y);
46     for(int i=1;i<=n;++i) f[i]=i;
47     for(int i=1,u,v,d,eu,ev;i<=m;++i){
48         scanf("%d%d%d",&u,&v,&d);
49         add_edge(u,v,d);add_edge(v,u,d);
50         eu=Find(u);ev=Find(v);
51         if(eu!=ev)
52             f[eu]=ev;
53     }
54     for(int i=1;i<=n;++i)
55         if(i==Find(i))
56             dfs1(i,0,++tcnt);
57     for(int i=1;i<=n;++i)
58         dfs(i,0,0);
59     int now=min(tcnt*X,Y);
60     dp[now][0]=1;dp[now][1]=tcnt*X;
61     
62     //设 dp[i][0/1] 表示 和为 i 的方案数/方案和, 
63     //g[i][0/1] 表示当前小树内长度为 i 的路径数/长度和
64     for(int i=1;i<=tcnt;++i){
65         for(int j=now;j<=Y;++j){
66             g[j][0]=dp[j][0];
67             g[j][1]=dp[j][1];
68             dp[j][0]=dp[j][1]=0;
69         }
70         for(int j=0;j<=Y;++j){
71             if(tot[i][j]){
72                 for(int k=now;k<=Y;++k)
73                     if(g[k][0]){
74                         dp[min(j+k,Y)][0]=(dp[min(j+k,Y)][0]+1ll*g[k][0]*tot[i][j])%Mod;
75                         dp[min(j+k,Y)][1]=(dp[min(j+k,Y)][1]+1ll*g[k][0]*sum[i][j]+1ll*g[k][1]*tot[i][j])%Mod;
76                     }
77             }
78         }
79     }
80     ans=dp[Y][1];
81     for(int i=1;i<tcnt;++i) ans=ans*i%Mod;//环的全排列
82     printf("%lld
",ans*500000004%Mod);  //减去环从前向后与从后向前相同的情况
83     return 0;
84 }
原文地址:https://www.cnblogs.com/LI-dox/p/11225952.html