[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;
}
posted @ 2020-02-02 17:34  LanrTabe  阅读(205)  评论(0编辑  收藏  举报