[题解]gdfzoj 723 | 2020提高训练12T3

传送门

首先,吐槽一下这道题:

为啥去买东西还往回走的啊,为啥买水果还能往回走的啊,奇怪的买东西方式增加了

第一次看到这道题的时候,没理解题意,以为不能回头走,想着树剖秒了

但是样例说明,可以往后走 自闭

正片

之后思考了一下,发现题目实际是求这个东西:存不存在有 (x) 个连通的点,其中有 (y) 个水果店

把样例图建出来之后发现了一个奇怪的现象,即

在同一棵子树里选固定个商铺,其中水果店的个数在一个区间里

(我也不知道表达地是否准确,反正差不多理解就行)

然后感觉可以 (DP) 试一下

(fmax[u][i]) 表示 (u) 节点的子树中选取 (i) 个点(包括 (u) 节点) , 的情况下最多的水果店数

(fmin[u][i]) 表示 (u) 节点的子树中选取 (i) 个点(包括 (u) 节点) , 的情况下的最少水果店数

更新:

(size[u])(u) 的子树大小

对于 (1 leq k leq size[u])(1 leq j leq size[v])

[ fmax[u][k+j]=max(fmax[u][k+j],fmax[u][k]+fmax[v][j]); ]

[ fmin[u][k+j]=min(fmin[u][k+j],fmin[u][k]+fmin[v][j]); ]

这个地方有点类似于 (01) 背包(反正我是这么理解的)

需要注意的坑点是 (k) 从大到小枚举 , (j) 从小到大枚举,因为不能覆盖之前的

得到 (fmax)(fmin)之后,就意味着从 (fmax[u][k])(fmin[u][k]) 的区间是合法的

所以将 (fmax)(fmin) 的值更新到 (dpmax)(dpmin) 里就可以了

假设询问为 (x y)

(dpmax[x]) 表示 (y) 可能的最大值

(dpmin[x]) 表示 (y) 可能的最小值

询问的时候判断 (y) 是不是在 (dpmax[x])(dpmin[x]) 之间就可以了

然后另外的一些坑点:

  1. (size[u]) 要边 (dfs) 边更新,因为如果先预处理 (size[u]) 的话会更新时出错(自行思考)

2.要先把 (fmin)(dpmin) 赋值无限大

然后就没啥好说的了吧。。

至于复杂度嘛,我也不知道,不过目测是 (O(n^3)) 的 ((dfs) * (for) * (for)

完结撒花

代码:


#include<bits/stdc++.h>
namespace my_std
{
    using namespace std;
	#define int long long
    #define R register
    #define rep(i,a,b) for (R int i=(a);i<=(b);i++)
    #define drep(i,a,b) for (R int i=(a);i>=(b);i--)
    #define go(u) for (R int i=head[(u)];i;i=e[i].nxt)
    #define pf printf
    #define writeln(x) write(x),putchar('
')
    #define writesp(x) write(x),putchar(' ')
    #define mem(x,v) memset(x,v,sizeof(x))
    typedef long long ll;
    const int INF=0x7fffffff;
    inline int read()
    {
        int sum=0,f=0;
        char c=getchar();
        while (!isdigit(c))
        {
            f|=(c=='-');
            c=getchar();
        }
        while (isdigit(c))
        {
            sum=(sum<<1)+(sum<<3)+(c^48);
            c=getchar();
        }
        return f?-sum:sum;
    }
    void write(int k)
    {
        if (k<0) putchar('-'),k=-k;
        if (k>=10) write(k/10);
        putchar(k%10+'0');
    }
    inline void chkmax(int &x,int y)
    {
    	if (x<y) x=y;
    }
    inline void chkmin(int &x,int y)
    {
    	if (x>y) x=y;
    }
}
using namespace my_std;
const int N=410;
int n,q,cnt,head[N];
int f_max[N][N],f_min[N][N];
int dpmax[N],dpmin[N];
int fruit[N],size[N];
struct edge
{
	int to,nxt;
}e[N<<1];
inline void add(int u,int v)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
inline void dfs(int u,int fa)
{
	size[u]=1;
	f_max[u][1]=f_min[u][1]=(fruit[u])?1:0;
	go(u)
	{
		int v=e[i].to;
		if (v==fa) continue;
		dfs(v,u);
		drep(k,size[u],1) rep(j,1,size[v])
		{
			chkmax(f_max[u][k+j],f_max[u][k]+f_max[v][j]);
			chkmin(f_min[u][k+j],f_min[u][k]+f_min[v][j]);
		}
		size[u]+=size[v];
	}
}
signed main()
{
	n=read(),q=read();
	rep(i,1,n-1)
	{
		int u=read(),v=read();
		add(u,v),add(v,u);
	}
	rep(i,1,n) fruit[i]=read();
	mem(dpmin,0x3f);mem(f_min,0x3f);
	dfs(1,0);
	rep(u,1,n) rep(i,1,size[u])
	{
		chkmax(dpmax[i],f_max[u][i]);
		chkmin(dpmin[i],f_min[u][i]);
	}
	rep(i,1,q)
	{
		int x=read(),y=read();
		if (dpmin[x]<=y&&dpmax[x]>=y) pf ("Yes
");
		else pf ("No
");
	}
}

(NOIP 2020 rp++)

原文地址:https://www.cnblogs.com/ZHANG-SHENG-HAO/p/12582954.html