1 条题解

  • 0
    @ 2025-8-24 21:50:29

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar BJpers2
    **

    搬运于2025-08-24 21:50:29,当前版本为作者最后更新于2018-07-31 20:25:02,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    我们在做题之前,先要知道这是一道图论题

    题意描述有些绕,我在这里再阐述一遍:

    • 给出一个部分未知的数列的,以及数列已知的部分
    • 再给出一些区间。对于每一个区间,在它的内部钦定一些位置,并要求这些位置上的数最后的值,都严格大于区间内其他未钦定的位置上的数。
    • 要求给出任意一种可行的满足条件的数列。

    大于关系可以看做是一条边,由较大的数指向较小的数。对于每一个询问,我们考虑让这k个位置上的数与区间内其他的位置两两连有向边。这样一来,问题就转化到图上了。

    首先,图上若有环则一定无解,因为它意味着x>xx>x

    那么问题变为DAG上问题,只要我们从入读为0的位置开始DP即可。贪心的想,最大的数越大,留给下面的空间就越大(注意数列中的数大于0!),所以每个数的可能值全部设置为1e9。DP时假如遇到了已经确定的数,却发现它撑死了也打不到他预设的值(否则与其他单调关系矛盾),那么问题无解。

    最后若有解,输出答案即可。

    好完美啊,在你看到k300000\sum k\leq 300000之前还以为是提高组水题。

    假如按之前说的那样建图,一次操作就要加入k(rl+1k)k(r-l+1-k)条边,最坏情况下加入的边数在N3N^3级别,空间直接爆炸。

    也许你会说,我们可以采用“电话交换机”的建图思想,不再两两连边 而是新拉出一个超级节点,然后只需要k个点向它连边,它向区间内其余点连边即可,只要k+(rl+1k)=rl+1k+(r-l+1-k)=r-l+1条边即可

    然而这还是不行。因为如果每个操作区间都是上十万的大区间,而k每次只有1,最多还是要连NkN\sum k条边,仍然爆炸。

    注意到,每次我们的空间浪费在,有大量位置连续的点,超级节点却向它们一一连了边。发挥想象力,有什么东西能提高区间操作的效率呢?

    线段树!!!

    没错,我们把数列建成一棵线段树,然后对于每个操作区间,它会被k个点割成最多k+1个子区间,对于每个区间,可以化成线段树上的最多log(n)log(n)个已知区间,对于超级节点连出的边,一次操作要加klog(n)klog(n)条,算上连向超级节点的,总共是k+klog(n)k+klog(n)。于是总共的边数在(k)log(n)(\sum k)log(n)级别,完全可以接受。

    这样连边以后,要注意线段树自身的边只是形式,要赋权为0。

    之后按照之前所说的,按拓扑序DP就行了。

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #define lson ls[u],l,md
    #define rson rs[u],md+1,r 
    #define FOR(i,a,b) for(int i=a;i<=b;i++)
    #define REP(u) for(int i=hd[u],v=e[i].v,w=e[i].w;i;i=e[i].n,v=e[i].v,w=e[i].w) 
    using namespace std;
    const int N=100100,TO=600600,M=7007000,INF=1000000000;
    struct edge{int n,v,w;}e[M];
    int n,s,m,p,v,u,x,k,l,r,pr,cnt,fl,ok,id[N],ins[TO];
    int hd[TO],vis[TO],in[TO],f[TO],ls[TO],rs[TO],o[TO];
    void add(int u,int v,int w){e[++fl]=(edge){hd[u],v,w};hd[u]=fl;in[v]++;}
    queue<int>q; 
    int read(){
        char ch=getchar();int x=0,o=1;
        for(;ch<'0' || '9'<ch;ch=getchar()) if(ch=='-') o=-1;
        for(;'0'<=ch&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^'0');
        return x*o;
    }
    void prg(){
        FOR(x,1,cnt){
            cout<<x<<':';
            REP(x) cout<<v<<'-'<<w<<' ';cout<<endl;
        }
    }
    void bld(int u,int l,int r){
        if(l==r) {id[l]=u;cnt=max(cnt,u);return;}
        int md=l+r>>1;
        ls[u]=++cnt,rs[u]=++cnt;
        bld(lson),bld(rson);
        add(u,ls[u],0),add(u,rs[u],0); 
    }
    void adde(int u,int l,int r,int x,int y,int z){
        if(x<=l && r<=y) {add(z,u,1);return;}
        if( y<l || r<x ) return;
        int md=l+r>>1;
        adde(lson,x,y,z),adde(rson,x,y,z);
    }
    void dfs(int u){
        vis[u]=1;ins[u]=1;
        REP(u){
            if(ins[v]) ok=0;
            if(!vis[v]) dfs(v);
        }
        ins[u]=0;
    } 
    int main(){
        scanf("%d%d%d",&n,&s,&m); 
        cnt=1,bld(1,1,n);
        FOR(i,1,s) p=read(),f[id[p]]=read(),o[id[p]]=f[id[p]];
        FOR(i,1,m){
            l=read(),r=read(),k=read();
            pr=l;++cnt;
            FOR(j,1,k){
                x=read();
                add(id[x],cnt,0);
                if(pr<=x-1) adde(1,1,n,pr,x-1,cnt);
                pr=x+1; 
            }if(pr<=r) adde(1,1,n,pr,r,cnt);
        } 
        FOR(i,1,cnt) if(!f[i]) f[i]=INF;
        FOR(i,1,cnt) if(!vis[i]){
            ok=1;dfs(i);
            if(!ok){printf("NIE");return 0;}
        }
        FOR(i,1,cnt) if(!in[i]) q.push(i);
        while(!q.empty()){
            u=q.front();q.pop();
            REP(u){
                in[v]--;
                if(o[v]>f[u]-w){printf("NIE");return 0;}
                f[v]=min(f[v],f[u]-w);
                if(!in[v]) q.push(v);
                if(f[v]<1){printf("NIE");return 0;}  
            }
        } 
        printf("TAK\n");
        FOR(i,1,n) printf("%d ",f[id[i]]);
    }
    
    • 1

    信息

    ID
    2661
    时间
    1000ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者