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=;register char c=getchar();for(;!isdigit(c);c=getchar());
for(;isdigit(c);now=now*+c-'',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\n", x)
#define Prl(x) printf("%lld\n",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-;
const int maxn = 5e3 + ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,K;
struct Edge{
int to,next;
}edge[maxn * ];
int head[maxn],tot;
void init(){
for(int i = ; i <= N ; i ++) head[i] = -;
tot = ;
}
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][] = MIN[t][] = color[t];
size[t] = ;
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 = ; j <= size[t] + size[v]; j ++){
oMIN[t][j] = INF; oMAX[t][j] = ;
}
for(int p = ; p <= size[v]; p ++){
for(int q = ; 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 = ; p <= size[t]; p ++){
MAX[t][p] = oMAX[t][p];
MIN[t][p] = oMIN[t][p];
}
}
for(int i = ; 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 = ; i <= N - ; i ++){
int u,v; Sca2(u,v);
add(u,v); add(v,u);
}
for(int i = ; i <= N ; i ++){
L[i] = INF; R[i] = -INF;
}
for(int i = ; i <= N ; i ++) Sca(color[i]);
dfs(,-);
for(int i = ; i <= M ; i ++){
int u,v; Sca2(u,v);
if(L[u] <= v && v <= R[u]) puts("Yes");
else puts("No");
}
}
return ;
}