题目链接:Luogu P6044
强行卡内存差评
首先对于一个固定的(v),(u)显然是有单调性的:若一个点(u)满足条件,则(u)的所有祖先都满足条件
那么就有了一个简单的做法,枚举所有的(v),找到最深的(u),对答案的贡献就是(u)的深度
我们可以用倍增来加速这个过程,即不断向上跳找到最浅的点(x)不满足条件,那么(x)的父亲就是最深的(u)。
倍增数组就有两个,一个(f[i][x])表示(x)的(2^i)级祖先,一个(g[i][x])表示从(x)向上(2^i)个点的或和。
然后问题来了,出题人卡内存怎么办?(倍增数组就要(152M))
我的方法是在DFS的过程中用一个栈维护根到(v)的这条链,实时维护(g)数组
这样的话我们就不需要(f)数组了!(直接在栈里往前移(2^i)个位置就好了)
这样就可以不特判过Subtask2了!
不知道为什么我的DFS会用掉(60M)的栈空间。。没办法只能手动模拟DFS了,极限数据约(100M)内存。
时间复杂度 (O(nlog n))
空间复杂度 (O(nlog n))
代码:
//不带IO优化的版本
#include <cstdio>
#define rint register int
typedef long long ll;
const int N=1000005;
int n,k,c[N],s[N],Top;
int Head[N],Next[N],To[N],En;
int f[20][N];
bool Son[N];
ll Ans;
inline void Add(int x,int y){Next[++En]=Head[x],To[Head[x]=En]=y;}
void Calc(int x)//将点x入栈并计算x的贡献
{
s[++Top]=x,f[0][Top]=c[x];
for(int i=1;1<<i<=Top;++i)f[i][Top]=f[i-1][Top]|f[i-1][Top-(1<<(i-1))];
int p=Top+1,v=0;
for(int i=19;i>=0;--i)
if((1<<i)<p&&(f[i][p-1]|v)<k)
v|=f[i][p-1],p-=1<<i;
Ans+=p-1;
}
void BFS(int x)//并不是BFS
{
Calc(x);
while(Top)
if(Head[x=s[Top]])Calc(To[Head[x]]),Head[x]=Next[Head[x]];//向下DFS
else --Top;//回溯
}
int main()
{
scanf("%d%d",&n,&k);
for(rint i=1;i<=n;++i)scanf("%d",&c[i]);
for(rint i=2,x,y;i<=n;++i)scanf("%d%d",&x,&y),Add(x,y),Son[y]=true;
for(rint i=1;i<=n;++i)if(!Son[i])BFS(i),i=n;
return printf("%lld
",Ans),0;
}