1 条题解

  • 0
    @ 2025-8-24 22:33:50

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 囧仙
    你做东方鬼畜音MAD,好吗?

    搬运于2025-08-24 22:33:50,当前版本为作者最后更新于2021-09-23 21:54:15,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    题解

    简化题面

    根据题意,在第 ii 块贤者之石里的能量若会向左传递,当且仅当 wi1<wi+1w_{i-1}<w_{i+1},因此我们从 i+1i+1i1i-1 连边;若会向右传递,当且仅当 wi1>wi+1w_{i-1}>w_{i+1},因此我们从 i1i-1i+1i+1 连边。

    可以发现,最终连出来的边里,只会是奇数编号的点连向奇数编号的点、偶数编号的点连向偶数编号的点。考虑分为奇偶两块来考虑。又能发现,单独抽出奇数/偶数的节点,那么这些边连出来的必然是链的形状。

    $$\boxed{1} \leftarrow \boxed{2} \phantom{\leftrightarrow} \boxed{3} \rightarrow \boxed{4} \leftarrow \boxed{5} \leftrightarrows \boxed{6} $$

    对于这张图,可以发现由于 w5>w6,w6>w5w_5>w_6,w_6>w_5 产生了矛盾,因此必然无解。

    那么考虑,如果一张图有解,我们怎么确定它的最小字典序呢?

    使用贪心。从左往右考虑每个点能被标注的最小的值。使用 cnt\mathit{cnt} 表示当前标注了编号的点的数目(下文构造方案可以证明,此时 1cnt1\sim \mathit{cnt} 的标号都被用过了)。但是可能出现如下状况:

    $$\boxed{1} \rightarrow \boxed{2} \rightarrow \boxed{3} \leftarrow \boxed{4} $$

    如果我们把 11 标为 cnt+1\mathit{cnt}+1 ,那么点 22 和点 33 就没办法标了。因此,我们应该把 33 标为 cnt+1\mathit{cnt}+1,把 22 标为 cnt+2\mathit{cnt}+2,最后把 11 标为 cnt+3\mathit{cnt}+3。容易发现,不存在一种更好的方案使得 11 标到的数字更小了。然后更新 cnt\mathit{cnt}

    因为有两条链,所以处理完一条链后记得转换到另外一条链上。如果一个点已经被标号了,那就直接跳过。


    然后我们要做的就是对于每个条件,将对应的点连边。首先我们肯定得按照奇偶分开。

    $$\boxed{2} \phantom{\leftrightarrow} \boxed{4} \phantom{\leftrightarrow} \boxed{6} \phantom{\leftrightarrow} \boxed{8} \\[1em] \boxed{1} \phantom{\leftrightarrow} \boxed{3} \phantom{\leftrightarrow} \boxed{5} \phantom{\leftrightarrow} \boxed{7} \phantom{\leftrightarrow} \boxed{9} $$

    使用数组 A,BA,B,若 Ai=trueA_i=\verb!true! 则表示链上 iii+2i+2 有一条边;同样地,若 Bi=trueB_i=\verb!true! 则表示链上 i+2i+2ii 有一条边。

    以该图举例。假设有个条件 {s=3,t=6}\{s=3,t=6\},应该连的边为:

    $$\boxed{2} \rightarrow \boxed{4} \rightarrow \boxed{6} \phantom{\leftrightarrow} \boxed{8} \\[1em] \boxed{1} \phantom{\leftrightarrow} \boxed{3} \rightarrow \boxed{5} \phantom{\leftrightarrow} \boxed{7} \phantom{\leftrightarrow} \boxed{9} $$

    其中,A2,A3,A4A_2,A_3,A_4 应当被赋值为 true\verb!true!。容易证明,需要赋值的部分的下标,应当为 s1,s,,t2s-1,s,\cdots,t-2

    假设有个条件 {s=7,t=2}\{s=7,t=2\},应该连的边为:

    $$\boxed{2} \leftarrow \boxed{4} \leftarrow \boxed{6} \leftarrow \boxed{8} \\[1em] \boxed{1} \phantom{\leftrightarrow} \boxed{3} \leftarrow \boxed{5} \leftarrow \boxed{7} \phantom{\leftrightarrow} \boxed{9} $$

    其中,B2,B3,B4,B5,B6B_2,B_3,B_4,B_5,B_6 应当被赋值为 true\verb!true!。容易证明,需要赋值的部分的下标,应当为 t,t+1,,s1t,t+1,\cdots,s-1

    考虑使用差分数组进行区间赋值为 true\verb!true!。如果我们要对某个数组 XX[l,r][l,r] 段区间赋值为 true\verb!true!,可以先维护它的差分数组 XX',让 XlXl+1,Xr1Xr11X'_l\gets X'_l+1,X'_{r-1}\gets X'_{r-1}-1。然后对它求前缀和,就能求出 XX 数组。

    参考代码

    #include<bits/stdc++.h>
    #define up(l,r,i) for(int i=l,END##i=r;i<=END##i;++i)
    #define dn(r,l,i) for(int i=r,END##i=l;i>=END##i;--i)
    using namespace std;
    typedef long long i64;
    const int INF =2147483647;
    int qread(){
        int w=1,c,ret;
        while((c=getchar())> '9'||c< '0') w=(c=='-'?-1:1); ret=c-'0';
        while((c=getchar())>='0'&&c<='9') ret=ret*10+c-'0';
        return ret*w;
    }
    const int MAXN =1e5+3;
    int n,t,q,A[MAXN],B[MAXN],N[MAXN]; bool f;
    int main(){
        up(1,qread(),T){
            n=qread(),q=qread(),f=false,t=0;
            memset(A,0,sizeof(int)*(n+1));
            memset(B,0,sizeof(int)*(n+1));
            memset(N,0,sizeof(int)*(n+1));
            up(1,q,i){
                int s=qread(),t=qread();
                if(s<t) ++A[s-1],--A[t-1]; else if(s>t) ++B[t],--B[s];
            }
            up(1,n,i) A[i]+=A[i-1],B[i]+=B[i-1];
            up(1,n-2,i) if(A[i]&&B[i]) f=true;
            if(f){puts("QED");continue;}
            up(1,n,i) if(!N[i]){
                int c=1; for(int j=i;A[j]&&j+2<=n;j+=2) ++c;
                up(1,c,j) N[i+2*c-2*j]=++t;
            }
            up(1,n,i) printf("%d%c",N[i],i==n?'\n':' '); 
        }
        return 0;
    }
    
    • 1

    信息

    ID
    7177
    时间
    1000ms
    内存
    125MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者