https://www.codechef.com/problems/BLACKCOM
题意:一颗5000个黑白结点的树,10W个查询寻找是否存在大小s并且有t和黑节点的子图
一开始就觉得应当是一个树dp,但是总觉得怎么做怎么超时,用dp[5000][5000]预处理s大小t结点的可行性在时间复杂度上并不合理。
但是这题有一个结论,对于树上一个大小为s的子图而言,如果同时有可以存在r个黑色节点和l个黑色节点,则l - r之间的所有黑色节点都可以构造。
有了这个结论,就可以把dp方程转变为t结点的子树下大小为s最多的黑色和最少的黑色,然后取他们之间的值即可。
细节在于子树之间的处理,dfs的时候同一颗子树是不能将子图加起来的,需要另开一个数组更新当前子树对答案的更新结果,最后统一赋值给最终答案。
#include <map> #include <set> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <vector> #include <string> #include <cstdio> #include <cstdlib> #include <cstring> #include <sstream> #include <iostream> #include <algorithm> #include <functional> using namespace std; inline int read(){int now=0;register char c=getchar();for(;!isdigit(c);c=getchar()); for(;isdigit(c);now=now*10+c-'0',c=getchar());return now;} #define For(i, x, y) for(int i=x;i<=y;i++) #define _For(i, x, y) for(int i=x;i>=y;i--) #define Mem(f, x) memset(f,x,sizeof(f)) #define Sca(x) scanf("%d", &x) #define Sca2(x,y) scanf("%d%d",&x,&y) #define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z) #define Scl(x) scanf("%lld",&x); #define Pri(x) printf("%d ", x) #define Prl(x) printf("%lld ",x); #define CLR(u) for(int i=0;i<=N;i++)u[i].clear(); #define LL long long #define ULL unsigned long long #define mp make_pair #define PII pair<int,int> #define PIL pair<int,long long> #define PLL pair<long long,long long> #define pb push_back #define fi first #define se second typedef vector<int> VI; const double eps = 1e-9; const int maxn = 5e3 + 10; const int INF = 0x3f3f3f3f; const int mod = 1e9 + 7; int N,M,K; struct Edge{ int to,next; }edge[maxn * 2]; int head[maxn],tot; void init(){ for(int i = 1; i <= N ; i ++) head[i] = -1; tot = 0; } void add(int u,int v){ edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++; } int color[maxn]; int MAX[maxn][maxn],MIN[maxn][maxn]; int oMAX[maxn][maxn],oMIN[maxn][maxn]; int L[maxn],R[maxn],size[maxn]; void dfs(int t,int la){ MAX[t][1] = MIN[t][1] = color[t]; size[t] = 1; for(int i = head[t]; ~i; i = edge[i].next){ int v = edge[i].to; if(v == la) continue; dfs(v,t); for(int j = 1; j <= size[t] + size[v]; j ++){ oMIN[t][j] = INF; oMAX[t][j] = 0; } for(int p = 0; p <= size[v]; p ++){ for(int q = 1; q <= size[t]; q ++){ oMAX[t][p + q] = max(MAX[t][q] + MAX[v][p],oMAX[t][p + q]); oMIN[t][p + q] = min(MIN[t][q] + MIN[v][p],oMIN[t][p + q]); } } size[t] += size[v]; for(int p = 1; p <= size[t]; p ++){ MAX[t][p] = oMAX[t][p]; MIN[t][p] = oMIN[t][p]; } } for(int i = 1; i <= size[t]; i ++){ L[i] = min(L[i],MIN[t][i]); R[i] = max(R[i],MAX[t][i]); } } int main(){ int T; Sca(T); while(T--){ Sca2(N,M);init(); for(int i = 1; i <= N - 1; i ++){ int u,v; Sca2(u,v); add(u,v); add(v,u); } for(int i = 1; i <= N ; i ++){ L[i] = INF; R[i] = -INF; } for(int i = 1; i <= N ; i ++) Sca(color[i]); dfs(1,-1); for(int i = 1; i <= M ; i ++){ int u,v; Sca2(u,v); if(L[u] <= v && v <= R[u]) puts("Yes"); else puts("No"); } } return 0; }