[Luogu P6044]「ACOI2020」惊吓路径
题目链接: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(n\log n)\)
空间复杂度 \(O(n\log 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\n",Ans),0;
}