@description@

众所周知小G热衷于搏弈,有一天他来到你的大草原上,抢走了你所有的牛羊,以供他搏弈。

你大惊失措,赶紧来到小G家拯救你的牛羊,然而,当你赶到的时候,你的牛已经变成了搏弈牛,你的羊已经变成了搏弈羊。你怒不可遏,决定拆了小G的家。

小G的家有 n 个房间,n−1 条走廊连接这些房间使得房间之间两两可达。在每一间房间里可能有一只牛,或者一只羊,或者一只小G,注意由于小G会分身术,所以可以有多个小G。

经过慎重考虑,你决定选择一些房间来攻击,你选择的房间必须满足两两之间可以不通过你未选择的房间到达。当你攻击时,你选择的房间中的所有牛、羊和小G都会来与你搏弈,由于人的力量是有限的,你只能应对 A 只牛,B 只羊,C 只小G。也就是说你能够同时应对 u 只牛,v 只羊和 w 只小G当且仅当 u≤A 且 v≤B 且 w≤C。

你想知道有多少种选择房间的方案使得你可以应对他们,输出其对 998244353 取模的结果。

@Input format@

第一行四个数 n,A,B,C,含义如题。

第二行 n 个数,第 i 个数表示第 i 间房里是什么:0 代表牛,1 代表羊,2 代表小G。

接下来 n−1 行,每行两个数 u,v,表示 u 到 v 之间有一条走廊。

@Output format@

一个数表示答案。

@Sample input@

5 1 1 1

0 1 0 0 2

1 2

1 3

3 4

4 5

@Sample output@

7

@Constraints@

对于所有的数据,n≤200,A+B+C≤n/2。

@solution@

任轩笛的论文《解决树上连通块问题的一些技巧和工具》中有提到与这道题类似的题并给出了解决方案:

对于一类树上连通块DP问题,如果信息的合并不够高效,但加入单点的信息比较高效,往往可以按照DFS序转移,把合并子树变成添加单点。

先假定选出的连通块必须包含根,求出整棵树的DFS序,一个包含根的树上连通块的结构一定形如整棵树去掉若干个互不相交的子树。在DFS序中,去掉的就是连续的若干段。

设 dp[i] 表示考虑了DFS序前 i 个节点时的信息。如果要选择第 i 个节点,则转移到 dp[i+1],否则转移到 dp[j],其中 j 是第 i 个点子树DFS序的右端点+1,表示去掉这整个子树。

而对于选出的连通块可以不包含根的问题,只要进行点分治,每次把重心作为根,算

出强制包含重心的方案数,再把重心删除,对每个连通块递归做。

之后论文中给出的例题也与这道题大同小异。

题意:给出一棵树,每个点有个颜色。给出3种颜色 u, v, w ,求有多少个树上连通块满足里面颜色为 u, v, w 的点的个数分别为 a, b, c 。

做法:用 dp[x][i][j][k] 表示处理了以 x 为根的子树,三种颜色个数分别为 i, j, k 的方案数。如果使用三维FFT优化,复杂度能变成 O(nabc log abc) ,但是常数和码量都较大。考虑使用上述的点分治算法,配合按DFS序转移,复杂度为 O(nabc log n)。

然后论文的题解基本照搬过来即可。

因为 A + B + C <= n/2 <= 100,所以 A*B*C 最大时 A = B = C,可以得到 A*B*C 最大约可以取 3*10^4。

所以 O(nABC*logn) 是可以跑得过的。

所以这是一道论文题。

@accepted code@

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 200;
const int MOD = 998244353;
struct edge{
int to; edge *nxt;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0];
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->nxt = adj[v], adj[v] = p;
}
int siz[MAXN + 5], hvy[MAXN + 5], dfn[MAXN + 5], dcnt;
int a[MAXN + 5], n, A, B, C;
bool vis[MAXN + 5];
void dfs(int x, int fa) {
dfn[++dcnt] = x, siz[x] = 1, hvy[x] = 0;
for(edge *p=adj[x];p;p=p->nxt) {
if( vis[p->to] || p->to == fa ) continue;
dfs(p->to, x);
siz[x] += siz[p->to];
hvy[x] = max(hvy[x], siz[p->to]);
}
}
int GetG(int x, int fa, int tot) {
int ret = x; hvy[x] = max(hvy[x], tot - siz[x]);
for(edge *p=adj[x];p;p=p->nxt) {
if( vis[p->to] || p->to == fa ) continue;
int tmp = GetG(p->to, x, tot);
if( hvy[tmp] < hvy[ret] )
ret = tmp;
}
return ret;
}
int dp[MAXN + 5][34*34*35 + 5], ans = 0;
inline int id(int x, int y, int z) {
return x*(B+1)*(C+1) + y*(C+1) + z;
}
void divide(int x) {
vis[x] = true, dcnt = 0, dfs(x, -1);
for(int p=1;p<=dcnt+1;p++)
for(int i=0;i<=A;i++)
for(int j=0;j<=B;j++)
for(int k=0;k<=C;k++)
dp[p][id(i, j, k)] = 0;
dp[1][id(0, 0, 0)] = 1;
for(int p=1;p<=dcnt;p++)
for(int i=0;i<=A;i++)
for(int j=0;j<=B;j++)
for(int k=0;k<=C;k++) {
dp[p+siz[dfn[p]]][id(i, j, k)] = (dp[p+siz[dfn[p]]][id(i, j, k)] + dp[p][id(i, j, k)])%MOD;
if( a[dfn[p]] == 0 && i != A ) dp[p+1][id(i+1, j, k)] = (dp[p+1][id(i+1, j, k)] + dp[p][id(i, j, k)])%MOD;
if( a[dfn[p]] == 1 && j != B ) dp[p+1][id(i, j+1, k)] = (dp[p+1][id(i, j+1, k)] + dp[p][id(i, j, k)])%MOD;
if( a[dfn[p]] == 2 && k != C ) dp[p+1][id(i, j, k+1)] = (dp[p+1][id(i, j, k+1)] + dp[p][id(i, j, k)])%MOD;
}
for(int i=0;i<=A;i++)
for(int j=0;j<=B;j++)
for(int k=0;k<=C;k++)
ans = (ans + dp[dcnt + 1][id(i, j, k)])%MOD;
ans = (ans + MOD - 1)%MOD;
for(edge *p=adj[x];p;p=p->nxt)
if( !vis[p->to] ) divide(GetG(p->to, -1, siz[p->to]));
}
int main() {
scanf("%d%d%d%d", &n, &A, &B, &C);
for(int i=1;i<=n;i++)
scanf("%d", &a[i]);
for(int i=1;i<n;i++) {
int u, v; scanf("%d%d", &u, &v);
addedge(u, v);
}
dcnt = 0, dfs(1, -1), divide(GetG(1, -1, siz[1]));
printf("%d\n", ans);
}

@details@

所以多读书论文是有好处的。

只不过我估计 noi 上是不会出现这种题的吧。。。

@noi.ac - 442@ 牛羊被他抢了的更多相关文章

  1. # NOI.AC省选赛 第五场T1 子集,与&最大值

    NOI.AC省选赛 第五场T1 A. Mas的童年 题目链接 http://noi.ac/problem/309 思路 0x00 \(n^2\)的暴力挺简单的. ans=max(ans,xor[j-1 ...

  2. NOI.ac #31 MST DP、哈希

    题目传送门:http://noi.ac/problem/31 一道思路好题考虑模拟$Kruskal$的加边方式,然后能够发现非最小生成树边只能在一个已经由边权更小的边连成的连通块中,而树边一定会让两个 ...

  3. NOI.AC NOIP模拟赛 第五场 游记

    NOI.AC NOIP模拟赛 第五场 游记 count 题目大意: 长度为\(n+1(n\le10^5)\)的序列\(A\),其中的每个数都是不大于\(n\)的正整数,且\(n\)以内每个正整数至少出 ...

  4. NOI.AC NOIP模拟赛 第六场 游记

    NOI.AC NOIP模拟赛 第六场 游记 queen 题目大意: 在一个\(n\times n(n\le10^5)\)的棋盘上,放有\(m(m\le10^5)\)个皇后,其中每一个皇后都可以向上.下 ...

  5. NOI.AC NOIP模拟赛 第二场 补记

    NOI.AC NOIP模拟赛 第二场 补记 palindrome 题目大意: 同[CEOI2017]Palindromic Partitions string 同[TC11326]Impossible ...

  6. NOI.AC NOIP模拟赛 第一场 补记

    NOI.AC NOIP模拟赛 第一场 补记 candy 题目大意: 有两个超市,每个超市有\(n(n\le10^5)\)个糖,每个糖\(W\)元.每颗糖有一个愉悦度,其中,第一家商店中的第\(i\)颗 ...

  7. NOI.AC NOIP模拟赛 第四场 补记

    NOI.AC NOIP模拟赛 第四场 补记 子图 题目大意: 一张\(n(n\le5\times10^5)\)个点,\(m(m\le5\times10^5)\)条边的无向图.删去第\(i\)条边需要\ ...

  8. NOI.AC NOIP模拟赛 第三场 补记

    NOI.AC NOIP模拟赛 第三场 补记 列队 题目大意: 给定一个\(n\times m(n,m\le1000)\)的矩阵,每个格子上有一个数\(w_{i,j}\).保证\(w_{i,j}\)互不 ...

  9. NOI.AC WC模拟赛

    4C(容斥) http://noi.ac/contest/56/problem/25 同时交换一行或一列对答案显然没有影响,于是将行列均从大到小排序,每次处理限制相同的一段行列(呈一个L形). 问题变 ...

随机推荐

  1. VC开发多语言界面 多种方法(非常easy) 有源代码

    源代码地址(专业定制程序:MCU,Windows,Android .VC串口,Android蓝牙等不限.) (需源代码先留邮箱)先上图 1.通过遍历 得到全部控件ID号与TEXT,得到一个中文语言配置 ...

  2. MyBatis连接Neo4j问题记录:mapper参数传递(节点标签作为参数)

    MyBatis与Neo4j的连接我在上一篇做了,这是链接:https://blog.csdn.net/qq_34233510/article/details/82496101 上一篇中UserMapp ...

  3. oracle习题练习-表空间-用户-表-约束

    题一 1.       创建名字为hy_tablespace的表空间,默认大小为10M;@@ 2.       创建一个用户,用户名以自己名字命名,并指定命名空间为hy_tablespace;@@@@ ...

  4. 洛谷P1084 [NOIP2012提高组Day2T3]疫情控制

    P1084 疫情控制 题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控 ...

  5. POJ1635Subway tree systems

    Subway tree systems Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 8049   Accepted: 33 ...

  6. PHP获取真实客户端的真实IP的方法

    REMOTE_ADDR 是你的客户端跟你的服务器“握手”时候的IP.如果使用了“匿名代理”,REMOTE_ADDR将显示代理服务器的IP. HTTP_CLIENT_IP 是代理服务器发送的HTTP头. ...

  7. vue+ElementUI项目中,上传控件为必填项,上传图片后清空提示信息

    (ps:以下是我在项目中遇到得问题及解决方法,希望对你们有帮助.如果还有其他方法,可以留言,谢谢) 一个表单页面,使用element-ui中el-upload上传图片,此项为必填项,然后写了校验规则, ...

  8. HDU - 1724 Ellipse 自适应辛普森模板

    OJ 题解传送门 //Achen #include<algorithm> #include<iostream> #include<cstring> #include ...

  9. python 中if __name__=="__main__"

    if __name__=="__main__":表示当执行运行文件为当前代码所在文件时,则会执行if__name__=="__main__":后的语句. 如果这 ...

  10. java贪吃蛇小游戏详解

    https://blog.csdn.net/u011622021/article/details/81162083