[COCI2015-2016#1] UZASTOPNI
A_Sunny_Day · · 题解
[COCI2015-2016#1] UZASTOPNI
题目链接:COCI2015-2016#1UZASTOPNI
写在前面:我代码中数组空间的开大是因为这道题在我们校内 OJ 上加了一个数据更大的点
简化一下题意,意思就是选出来的权值要构成一个连续的序列。这里有一个重要的性质,我们从一棵子树入手,设这棵子树的根为
-
如果要选择 $son$ 这个点,那么 $rt$ 这个点就一定要选,同时由于集合中不能有**相同的权值**。所以在 $son$ 为根的子树里面不能选 $v_{rt}$ 这个权值,那么在以 $son$ 为根的子树里面选出权值构成的连续序列的最大值 $v_{max}$ 一定要小于 $v_{rt}$。我们可以用反证法来证明,如果 $v_{max}$ 被选了,由于 $v_{son}<v_{rt}<v_{max}$,为了构成一个连续的序列,我们必须选 $v_{rt}$,而我们又必须选 $rt$,这就矛盾了。 -
这种情况与上述相似,我们设以 $son$ 为根的子树选出权值构成的连续序列的最小值为 $v_{min}$。如果 $v_{min}<v_{rt}$,又因为 $v_{min}<v_{rt}<v_{son}$,所以以 $son$ 为根的子树里面一定要选 $v_{rt}$,而 $rt$ 也一定要选,这不符合题意,所以 $v_{min}>v_{rt}$。
那么我们得到了一个重要信息:对于一个子树的根
根据这个性质我们可以定下我们的 dp 数组。设
还有一点需要注意的是,我们 dp 的顺序也有讲究,合并权值小于
但是这样我们的时空都是
那么用 bitset 优化后时空都变成了
代码如下:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 1e5+5;
int n,v[MAXN];
vector <int> e[MAXN];
bitset<2005> L[MAXN],R[MAXN];
bool cmp(int x,int x_)
{
return v[x]<v[x_];
}
void dfs(int cur,int fa)
{
L[cur].set(v[cur]);R[cur].set(v[cur]);
for(int i=0;i<e[cur].size();++i)
{
int to=e[cur][i];
if(to==fa) continue;
dfs(to,cur);
}
int to;
for(int i=0;i<e[cur].size();++i)//升序
if(v[to=e[cur][i]]>v[cur]&&((R[cur]<<1)&L[to]).any()) R[cur]|=R[to];
for(int i=e[cur].size()-1;i>=0;--i)//降序
if(v[to=e[cur][i]]<v[cur]&&((L[cur]>>1)&R[to]).any()) L[cur]|=L[to];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&v[i]);
for(int i=1;i<n;++i)
{
int u,v;
scanf("%d %d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
for(int i=1;i<=n;++i)
sort(e[i].begin(),e[i].end(),cmp);
dfs(1,0);
printf("%lld",(ll)L[1].count()*(ll)R[1].count());
return 0;
}