[hackerrank w25]DAG Queries 解题报告

来源:互联网 发布:爱淘宝怎么卸载 编辑:程序博客网 时间:2024/05/18 03:16

题意:
给出一个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<stdio.h>#include<iostream>using namespace std;#include<algorithm>#include<cstring>#include<bitset>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<qtot)            {                //printf("Q:u=%d,i=%d,now=%d\n",que[i].u,que[i].i,ans[que[i].i]);                if(que[i].i<perform[r].i)                {                    int 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+=j<l;                }                else                    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
③对时间分块:对每个块处理出最终状态;操作对查询的影响是一个关于时间的点/区间。

0 0
原创粉丝点击