[HackerRank Week of Code]DAG Queries/[JZOJ5038]命运的构造

本文介绍了一种针对有向无环图(DAG)的操作优化算法,包括节点赋值与求最小值操作。通过定期重构与分块技术,在保证时间效率的同时解决了空间限制问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给定一个n个点m条边的有向无环图,每个点有初始点权ai=0,有q个操作,操作类型如下:
 1 u x:对于所有u可以到达的点vav变成x
 2 u x:对于所有u可以到达的点vav更新为min(av,x)
 3 x:询问ax

1n,m,q105,0ai109


题目分析

我看到这题的最初想法是:DAG上的修改很难使用传统的数据结构来维护,解法应该是对所有修改操作定期重构。
于是我想啊想,发现我如果使用bitset来压点与点之间的连通状态,询问似乎可以做,但是由于取min操作的特殊性,定期重构没有办法保证时间复杂度。

于是题解是一种特别强大的时间复杂度几乎和暴力没有区别的算法。
考虑对赋值操作定期重构,对取min操作进行分块。
fi,x表示第i块的二类操作对点x的最小影响。我们处理询问的时候先找到距离该询问最近的赋值操作,然后再通过分块找到这个赋值操作一直到当前询问这个区间内的最小的取min操作,然后就可以计算答案了。
重构的话,因为是操作一,所以很方便的啦。

然后,你以为这样就可以过了?O(n232)的空间是会MLE的。。。这就很尴尬了。
怎么办,我们只能牺牲一下时间复杂度来换取空间复杂度了。我们将所有点分块,每次只处理与块内的点有关的询问,储存连通性也只储存所有点与块内点的连通性。
考虑到这样会导致我们需要做很多次定期重构,应该将定期重构的块大小调大,并且点分块的大小也要在空间允许的情况下尽量大。
阈值取值比较好的话,时间复杂度可以做到O(n74)
所以说和暴力没有什么区别嘛


代码实现

#include <algorithm>
#include <iostream>
#include <climits>
#include <bitset>
#include <cstdio>
#include <cctype>
#include <queue>
#include <cmath>

using namespace std;

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int buf[30];

inline void write(int x)
{
    if (x<0) putchar('-'),x=-x;
    for (;x;x/=10) buf[++buf[0]]=x%10;
    if (!buf[0]) buf[++buf[0]]=0;
    for (;buf[0];putchar('0'+buf[buf[0]--]));
}

const int INF=INT_MAX/2;
const int N=100005;
const int M=100005;
const int Q=100005;
const int MAXA=350;
const int MAXB=2200;
const int MAXC=12000;

int last[N],deg[N],kth[N];
int tov[M],nxt[M];
int opt[Q][3];
int answer[Q];
int n,m,q,tot,idx;

queue<int> que;

inline void insert(int x,int y){tov[++tot]=y,nxt[tot]=last[x],last[x]=tot;}

/*
Partition the point set
*/

bitset<MAXC> con[N];
int C;

inline void Partition_Point_Set();
inline void Process_Connectedness(int l,int r);
inline void Solve_Query(int l,int r);

/*
Partition the queries of the second type
*/

int stt[MAXA],ent[MAXA];
int f[MAXA][N];
int srt[MAXA];
int mintag[N];
int op[Q][3];
int A,cnt;

inline void Partition_Operation();
inline void Process_Block(int bid,int l,int r);
inline int Query_Range(int l,int x,int st,int en);

/*
Periodic Reconstruction (the queries of the first type)
*/

int cache[MAXB][2];//time point
int asstag[N];//time value
int csize;
int B,btot;

inline void Initialization();
inline void Add_Operation(int id);
inline int Latest_Modification(int l,int x);
inline void Reconstruction();

void pre()
{
    idx=0;
    for (int x=1;x<=n;++x) if (!deg[x]) que.push(x);
    for (int x;!que.empty();)
    {
        kth[++idx]=x=que.front(),que.pop();
        for (int i=last[x],y;i;i=nxt[i])
            if (!--deg[y=tov[i]]) que.push(y);
    }
}

int main()
{
    freopen("destiny.in","r",stdin),freopen("destiny.out","w",stdout);
    n=read(),m=read(),q=read();
    for (int i=1,x,y;i<=m;++i) x=read(),y=read(),insert(x,y),++deg[y];
    pre();
    for (int i=1;i<=q;++i)
    {
        opt[i][0]=read(),opt[i][1]=read();
        if (opt[i][0]!=3) opt[i][2]=read();
    }
    Partition_Operation(),Partition_Point_Set();
    for (int i=1;i<=q;++i) if (opt[i][0]==3) write(answer[i]),putchar('\n');
    fclose(stdin),fclose(stdout);
    return 0;
}

/*----------------------------------------------------------------------------*/

inline void Partition_Operation()
{
    for (int i=1;i<=q;++i) if (opt[i][0]==2) op[++cnt][0]=i,op[cnt][1]=opt[i][1],op[cnt][2]=opt[i][2];
    A=trunc(sqrt(cnt))+1,btot=0;
    for (int lcur=1,rcur;lcur<=cnt;lcur=rcur+1)
    {
        rcur=min(lcur+A-1,cnt);
        stt[++btot]=op[lcur][0],ent[btot]=op[rcur][0];
        Process_Block(btot,lcur,rcur);
    }
}

inline bool cmp(int x,int y){return op[x][2]<op[y][2]||op[x][2]==op[y][2]&&op[x][0]>op[y][0];}

inline void Process_Block(int bid,int l,int r)
{
    for (int i=1;i<=n;++i) mintag[i]=INF;
    int tmp=0;
    for (int i=l;i<=r;++i) srt[++tmp]=i;
    sort(srt+1,srt+1+tmp,cmp);
    for (int j=1,x,tag;j<=tmp;++j)
    {
        if (mintag[x=op[srt[j]][1]]<=(tag=op[srt[j]][2])) continue;
        for (mintag[x]=tag,que.push(x);!que.empty();)
        {
            x=que.front(),que.pop();
            for (int i=last[x],y;i;i=nxt[i])
                if (mintag[y=tov[i]]>tag) mintag[y]=tag,que.push(y);
        }
    }
    for (int i=1;i<=n;++i) f[bid][i]=mintag[i];
}

inline int Query_Range(int l,int x,int st,int en)
{
    int ret=INF;
    for (int i=1,lcur=1,rcur;i<=btot;++i,lcur=rcur+1)
    {
        rcur=min(lcur+A-1,cnt);
        if (ent[i]<st) continue;
        if (stt[i]>en) break;
        if (stt[i]<=st)
        {
            for (int j=lcur;j<=rcur;++j)
                if (st<=op[j][0]&&op[j][0]<=en&&con[op[j][1]].test(x-l+1))
                    ret=min(ret,op[j][2]);
            continue;
        }
        if (en<=ent[i])
        {
            for (int j=lcur;j<=rcur;++j)
                if (op[j][0]<=en&&con[op[j][1]].test(x-l+1))
                    ret=min(ret,op[j][2]);
            continue;
        }
        ret=min(ret,f[i][x]);
    }
    return ret;
}

/*----------------------------------------------------------------------------*/

inline void Partition_Point_Set()
{
    C=30*trunc(sqrt(n))+1;
    for (int lcur=1,rcur;lcur<=n;lcur=rcur+1)
    {
        rcur=min(lcur+C-1,n);
        Process_Connectedness(lcur,rcur),Solve_Query(lcur,rcur);
    }
}

inline void Process_Connectedness(int l,int r)
{
    for (int x=1;x<=n;++x) con[x].reset();
    for (int j=n,x;j>=1;--j)
    {
        x=kth[j];
        if (l<=x&&x<=r) con[x].set(x-l+1);
        for (int i=last[x];i;i=nxt[i]) con[x]|=con[tov[i]];
    }
}

inline void Solve_Query(int l,int r)
{
    Initialization();
    for (int i=1;i<=q;++i)
        if (opt[i][0]==1) Add_Operation(i);
        else if (opt[i][0]==3&&l<=opt[i][1]&&opt[i][1]<=r)
        {
            int x=opt[i][1],lpass=Latest_Modification(l,x),mintg=Query_Range(l,x,lpass,i);
            answer[i]=min(opt[lpass][2],mintg);
        }
}

/*----------------------------------------------------------------------------*/

inline void Initialization()
{
    B=trunc(pow(q,2./3.))+1,csize=0;
    for (int i=1;i<=n;++i) asstag[i]=0;
}

inline void Add_Operation(int id)
{
    cache[++csize][0]=id,cache[csize][1]=opt[id][1];
    if (csize==B) Reconstruction();
}

inline int Latest_Modification(int l,int x)
{
    for (int i=csize;i>=1;--i) if (con[cache[i][1]].test(x-l+1)) return cache[i][0];
    return asstag[x];
}

inline void Reconstruction()
{
    for (int j=csize,x,tag;j>=1;--j)
    {
        if (asstag[x=cache[j][1]]>(tag=cache[j][0])) continue;
        for (asstag[x]=tag,que.push(x);!que.empty();)
        {
            x=que.front(),que.pop();
            for (int i=last[x],y;i;i=nxt[i])
                if (asstag[y=tov[i]]<cache[j][0]) asstag[y]=tag,que.push(y);
        }
    }
    csize=0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值