[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<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
③對時間分塊:對每個塊處理出最終狀態;操作對查詢的影響是一個關於時間的點/區間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章