[hackerrank w25]DAG Queries 解题报告

题意:
给出一个n个点,m条边的dag,每个点有点权 ai ,初始均为0。有q种操作,每种操作有3种类型:
1 u x:将所有u能到达的点的权值设为x。
2 u x:将所有u能到达的点的权值对x取min。
3 u:查询u的点权。
n,m,q105,n2,m,q1,1un,0x109

又用bitset强上了一道题好开心啊~(然而这题标算似乎就是bitset?)

如果暴力的话,可以用bitset,但是空间爆炸;所以我们先解决一下空间问题。
如果考虑对操作分块的话,我们可以只用处理所有节点能否由块内的操作节点抵达,这样首先就可以解决bitset的空间问题!

然后一开始我是这样想的:
如果我们可以 O(n) 处理出每一个块的操作结束后的 ai 的话,那么就好办了!而这个似乎很容易处理的样子。然后就各种YY,写了一发,但是搞了半天,发现实在是没法搞。。这个覆盖操作和取min操作在一起根本做不了。

但是我们仔细考虑一下,对u的询问的答案是能到达它的最后一个1操作的x和之间所有的2操作的x最小值。这样的话其实我们没必要处理出每个块的最终状态。我们只需要找到这最后一个1在哪个块里,这是可以对每个块用一个 O(n) 的类似于dp的东西处理出来的,于是两边的块便都可以 O(n) 暴力(在用bitset处理过连通性以后);中间的块只需要算出每个节点在这个块中会得到的2操作的最小值是多少即可,这个玩意儿也可以用一个类似 O(n) dp的东西搞出来。
时间复杂度 O(nn+n264)

看了题解,发现它是这么解决空间问题的:把所有的节点分成3份,对于暴力部分计算3次——这时我才突然明白,bitset可以开到 109

发现不知不觉似乎已经用这种类似对时间分块的方法a了很多题了。。而且做法主要是有两种,一种是处理出每个块的最终状态,这种适用于那种很多操作一起处理比较方便的,一种是查询的是一个点,那么我们可以分块找出这个点。
似乎如果允许离线,而且本来的点与点之间的关系比较奇怪,对时间分块还是一个不错的搞法呢~

代码:

#include
#include
using namespace std;
#include
#include
#include
char * cp=(char *)malloc(4000000);
inline void in(int &x)
{
    while(*cp<'0'||*cp>'9')++cp;
    for(x=0;*cp>='0'&&*cp<='9';)x=x*10+(*cp++^'0');
}

const int N=1e5+5,M=1e5+5,Q=1e5+5;
int nxt[M],succ[M],ptr[N],etot=1;
inline void addedge(int u,int v)
{
    nxt[etot]=ptr[u],ptr[u]=etot,succ[etot++]=v;
}

bool exist_deg[N];
int indeg[N];
int topo[N],ttot=1;
void topo_dfs(int node)
{
    topo[ttot++]=node;
    for(int i=ptr[node];i;i=nxt[i])
        if(--indeg[succ[i]]==0)
            topo_dfs(succ[i]);
}

struct PS
{
    int opt,u,x,i;
}perform[Q];
struct QS
{
    int u,i;
}que[Q];
int ans[Q];

const int B=316;
//const int B=4;
bitset<320> b[N];
bool cover[N];
int Min[N];
int main()
{
    freopen("dagq.in","r",stdin);
    freopen("dagq.out","w",stdout);
    fread(cp,1,4000000,stdin);
    int n,m,q;
    in(n),in(m),in(q);
    for(int u,v;m--;)
    {
        in(u),in(v);
        addedge(u,v);
        exist_deg[v]=1;
        ++indeg[v];
    }

    for(int i=n;i;--i)
        if(!exist_deg[i])
            topo_dfs(i);

    int ptot=0,qtot=0;
    for(int i=1;i<=q;++i)
    {
        in(perform[ptot].opt);
        if(perform[ptot].opt<=2)
        {
            in(perform[ptot].u),in(perform[ptot].x);
            perform[ptot++].i=i;
        }
        else
        {
            in(que[qtot].u);
            que[qtot++].i=i;
        }
    }

    memset(ans,127,sizeof(ans));

    //printf("ptot=%d,qtot=%d\n",ptot,qtot);

    for(int l=(ptot-1)-(ptot-1)%B,r=ptot-1;l>=0;r=l-1,l-=B)
    {
        /*printf("---[%d,%d]:[%d,%d]---\n",l,r,perform[l].i,perform[r].i);
        printf("perform=");
        for(int i=l;i<=r;++i)printf("(opt=%d,u=%d,x=%d,i=%d) ",perform[i].opt,perform[i].u,perform[i].x,perform[i].i);
        puts("");*/

        for(int i=n;i;--i)b[i].reset();
        for(int i=l;i<=r;++i)b[perform[i].u][i-l]=1;
        for(int i=1;i<=n;++i)
            for(int j=ptr[topo[i]];j;j=nxt[j])
                b[succ[j]]|=b[topo[i]];

        /*for(int i=1;i<=n;++i)
        {
            printf("%d:",topo[i]);
            for(int j=0;j<=r-l;++j)printf("%d",(int)b[topo[i]][j]);
            puts("");
        }*/

        memset(cover,0,sizeof(cover));
        for(int i=l;i<=r;++i)
            if(perform[i].opt==1)
                cover[perform[i].u]=1;
        for(int i=1;i<=n;++i)
            if(cover[topo[i]])
                for(int j=ptr[topo[i]];j;j=nxt[j])
                    cover[succ[j]]=1;

        memset(Min,127,sizeof(Min));
        for(int i=l;i<=r;++i)
            if(perform[i].opt==2)
                Min[perform[i].u]=min(Min[perform[i].u],perform[i].x);
        for(int i=1;i<=n;++i)
            for(int j=ptr[topo[i]];j;j=nxt[j])
                Min[succ[j]]=min(Min[succ[j]],Min[topo[i]]);

        {
            int i=qtot;
            while(i&&que[i-1].i>perform[l].i)--i;
            while(i//printf("Q:u=%d,i=%d,now=%d\n",que[i].u,que[i].i,ans[que[i].i]);
                if(que[i].iint j=r;
                    while(perform[j].i>que[i].i)--j;
                    for(;j>=l;--j)
                        if(b[que[i].u][j-l])
                        {
                            ans[que[i].i]=min(ans[que[i].i],perform[j].x);
                            if(perform[j].opt==1)
                            {
                                swap(que[i],que[--qtot]);
                                break;
                            }
                        }
                    i+=jelse
                    if(cover[que[i].u])
                    {
                        int j=r;
                        for(;perform[j].opt==2||!b[que[i].u][j-l];--j)
                            if(perform[j].opt==2&&b[que[i].u][j-l])
                                ans[que[i].i]=min(ans[que[i].i],perform[j].x);
                        ans[que[i].i]=min(ans[que[i].i],perform[j].x);

                        swap(que[i],que[--qtot]);
                    }
                    else
                    {
                        ans[que[i].i]=min(ans[que[i].i],Min[que[i].u]);
                        ++i;
                    }
            }
        }
    }
    while(qtot--)ans[que[qtot].i]=0;

    for(int i=1;i<=q;++i)
        if(ans[i]<=1e9)
            printf("%d\n",ans[i]);
}

总结:
①一定要想好细节再写代码!
②bitset占的内存是 18 ,char/bool是1,int是4,long long是8。所以bitset一般能开 109 ,char/bool能开 108 ,int能开 107/108 ,long long能开 107
③对时间分块:对每个块处理出最终状态;操作对查询的影响是一个关于时间的点/区间。

你可能感兴趣的:(bitset,分块,bitset,分块)