P8542 「Wdoi-2」魔力的雷云

题目背景

道具会自己活动,本来温顺的妖怪突然大闹起来原来都是因为万宝槌的影响。
唆使小人的天邪鬼虽然最后逃走了,但因为真正使用万宝槌的是小人,所以灵梦决定对其进行监视。但是总感觉也没再有什么不良企图的样子。

但就在那时,天空再次出现了诡异的云团。
是与逆城出现之时一样的,魔力风暴。

“那不是我干的”,少名针妙丸这样解释着
到底,又是谁再次掀起了这样的风暴?
灵梦她们,为了调查清楚原因,再次冲进了风暴之中。

小人的末裔、逆转的城、为反抗而反抗的天邪鬼。
这背后又隐藏着什么呢。

题目描述

简要题意

�n 块磁铁排列成了一个环。每块磁铁都有磁力,用 4��4ai​ 表示。相邻两块磁铁磁力大小不同。磁铁大小一致,长度均为 11 单位长度。摆放时分为北极朝左和北极朝右。当两块磁铁互相吸引时,它们会紧紧靠拢,互相排斥时,记这两块磁铁的磁力为 �u 和 �v,那么它们会相距 �+�u+v 单位长度。

目前已知整个环的长度(包括每个磁铁的长度,以及相邻磁铁之间的距离),记这个长度为 �0d0​ 个单位长度。对于第 �i 块磁铁,也已知若拿去这块磁铁,剩余磁铁组成的环的长度,记这个长度为 ��di​ 个单位长度。

找到找到任意一种可能的磁铁排列方法满足这些已知的数据,(也要满足相邻两块磁铁磁力大小不同),并且可以决定所用磁铁的磁力。保证有解。

原始题意

在堀川雷鼓引发的雷暴中,一架架周围附带着电磁场的鼓正随着节奏摇摆着。在强大的电磁干扰下,巫女和魔法使与地面失去了联系。

这些尺寸完全相同(可以视作为单位长度 11)的带有磁力的鼓一共有 �n 架,它们排列成了一个环。为了让弹幕美观,第 �i 架鼓上带有的磁力是 4��4ai​,而且相邻两架鼓带有的磁力大小不同。

每一架鼓都有磁性上的南北两极,而且在摆放的时候有北极朝左和北极朝右的两种情况。若两架鼓相互吸引它们会紧紧靠拢;而若它们相互排斥,这两架鼓之间的距离就是它们的磁力之和的单位长度。

灵梦记录下了整个环的长度 �0d0​,这包括了每架鼓的长度和相邻的鼓之间的距离,而魔理沙正在使用自己的火力击碎这些鼓。然而这些鼓的质量非常皮实,魔理沙一次只能击碎一个鼓,随后堀川雷鼓会用一个尺寸和磁力完全一致的鼓替代这个被击碎的鼓。但是在击碎第 �i 架鼓的时候,受到磁力的作用环的长度会发生变化,魔理沙会记录此时剩余的鼓组成的环的长度 ��di​(假设磁力的作用是瞬间完成的)。

在战斗的间隙,灵梦和魔理沙希望通过她们记录下的数据,找到任意一种鼓的排布情况满足这些数据。为了降低难度,她们可以自由决定每架鼓上的磁力多少。数据保证有解。

尽快解出鼓的分布,从而结束这场异变吧!

输入格式

  • 第一行有一个整数 �n,表示磁铁的总个数。
  • 接下来 11 行一个整数,表示 �0d0​。
  • 接下来 �n 行每行一个整数,其中第 �i 行的整数表示 ��di​。

输出格式

  • 输出共 �n 行。第 �i 行有两个整数 ��,��fi​,ai​,分别表示磁铁的方向与磁力。若 ��=0fi​=0 则表示北极向左,否则若 ��=1fi​=1 则表示北极向右。

输入输出样例

输入 #1复制

4
14
13
16
3
16

输出 #1复制

1 1
1 0
0 1
1 0

题解

考虑每个 ��di​ 到底可以给我们提供多少信息。具体而言,从左往右考虑每一块磁铁。每次选择三块磁铁进行讨论。

情况一:→→→→​→​→​

此时完全无法推断第二块和第三块磁铁的磁力大小,直接忽略。

情况二:→→←→​→​←​

  • 保留第二块磁铁,总贡献为 �=�+1+�+�+�x=p+1+b+c+q;
  • 拿掉第二块磁铁,总贡献为 �=�+�+�+�y=p+a+c+q。

做差,发现 �−�−1=�−�x−y−1=b−a。可以直接推断出第一块和第二块磁铁的磁力大小。

情况三:→←←→​←​←​

  • 保留第二块磁铁,总贡献为 �=�+�+�+1+�x=p+a+b+1+q;
  • 拿掉第二块磁铁,总贡献为 �=�+�+�+�y=p+a+c+q。

做差,发现 �−�−1=�−�x−y−1=b−c。可以直接推断出第二块和第三块磁铁的磁力大小。

情况四:→←→→​←​→​

  • 保留第二块磁铁,总贡献为 �=�+�+�+1+�+�+�x=p+a+b+1+b+c+q;
  • 拿掉第二块磁铁,总贡献为 �=�+�y=p+q。

做差,发现 �−�−1=�+2�+�x−y−1=a+2b+c。感觉不一定有用阿。但是只要我们知道了 �a 或者 �c 的大小,就可以计算出剩余磁铁的磁力大小。


但是从这些地方,我们可以发现一些结论:

  • 如果出现了形如 AAAAAAAAAA 形式的方向摆放,最中间的磁铁的磁力大小无需得知。因此可以将所有长度大于 44 的连续段缩成长度为 44 的连续段。
  • 如果出现了形如 ABBABB 形式的方向摆放,就可以一次性推断出后两块磁铁的磁力。
  • 如果出现了形如 BBABBA 形式的方向摆放,就可以一次性推断出前两块磁铁的磁力。
  • 结合上述三点,对于缩完之后的长度在 2∼42∼4 之间的连续段,如果它两端有磁铁,就可以一口气把这一段内所有磁铁的磁力计算出来。
  • 那么目前计算不出来磁力的磁铁,肯定都是长度为 11 的互相交错的连续段。他们的两侧就是磁力大小都被计算出来的磁铁,或者是整条磁铁链的顶端。
  • 那么结合情形 33,可以推知所有磁铁的磁力大小。

接着是特殊情况的讨论:

  • 如果所有磁铁方向相同,那么所有磁铁的磁力大小都是无关紧要的,可以随便赋值。
  • 排除以上一点,肯定会出现不同方向的磁铁。如果磁铁方向呈现了 ABAB⋯ABAB⋯ 的交替情形,那么我们只能枚举某个磁铁的磁力大小来破局。但是你没必要 �(log⁡�)O(logn) 枚举;事实上,由于情形 44,可以发现 �b 的可能性是非常有限的。
  • 排除以上两点,肯定会出现长度不小于 22 的连续段。可以从这里作为突破口,首先进行链的缩减,接着计算出所有 2∼42∼4 段磁铁的磁力大小,接着计算出所有磁铁的磁力大小。

初始时设第一块磁铁方向向左即可。容易发现这不会对结果产生任何影响。不过有可能出现第一块磁铁左右两侧恰好有一块方向和它不同,你却不知道是哪个。那也问题不大,对两种情况分别求解判断合法性即可。


如何判断连续段的长度:

  • 如果一个磁铁与周围两块磁铁方向都相同,那么拿掉他后,�d 只会减小 11;
  • 如果一个磁铁仅与周围一块磁铁方向不同,那么拿掉他后,�d 的减小值模 33 与 11 同余;
  • 如果一个磁铁与周围两块磁铁方向都不同,那么拿掉他后,�d 的减小值模 33 与 22 同余。

读者可以自行证明。这里不再赘述。


如何根据 �+2�,�≠�a+2b,a=b 知晓 �a 和 �b 的值:

  • 注意到 �,�a,b 均为 44 的整数次幂,可以设 �=22�,�=22�a=22u,b=22v,相加后为 22�+22�+122u+22v+1。那么找二进制下两个 11,偶数位置的 11 是属于 �a 的,奇数位置的 11 是属于 �b 的。

如何根据 �−�,�≠�a−b,a=b 知晓 �a 和 �b 的值:

  • 若 �−�>0a−b>0,那么 �=lowbit(�−�)b=lowbit(a−b),同时推出 �=(�−�)+�a=(a−b)+b。
  • 若 �−�<0a−b<0,那么取反后的值为 �−�>0b−a>0,根据上一种情况即可得解。

读者可以自行验证/证明,这里不再赘述。

代码

#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 unsigned int       u32;
typedef unsigned long long u64;
typedef long long i64;
const int INF =2147483647;
const int MAXN=1e6+3;
i64 D[MAXN],E[MAXN],A[MAXN]; bool F[MAXN]; int n,O[MAXN];
i64 qread(){
    i64 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;
}
i64 lowbit(i64 t){return t&-t;}
int countz(i64 t){return __builtin_ctzll(t);}
bool check(){
    A[0]=A[n],F[0]=F[n],A[n+1]=A[1],F[n+1]=F[1];
    i64 d0=n,d1=0,dn=0,dx=0;
    up(1,n,i){
        if(A[i]==-1) continue;
        if(A[i]!=(1ll<<(O[i]<<1))) return false;
        if(A[i]==A[i==1?n:i-1]||A[i]==A[i==n?1:i+1])
            return false;
    }
    up(1,n,i) if(F[i]!=F[i+1]) d0+=A[i]+A[i+1];
    if(d0!=D[0]) return false;
    up(1,n,i){
        dx=d0-1;
        if(F[i  ]!=F[i-1]) dx-=A[i  ]+A[i-1];
        if(F[i  ]!=F[i+1]) dx-=A[i  ]+A[i+1];
        if(F[i-1]!=F[i+1]) dx+=A[i-1]+A[i+1];
        if(dx!=D[i]) return false;
    }
    return true;
}
void find_add(i64 w,i64 &p,i64 &q){ // w=p+2q -> p,q
    i64 a=lowbit(w),b=w-a;
    if(countz(a)&1) q=a/2,p=b; else q=b/2,p=a;
}
void find_min(i64 w,i64 &p,i64 &q){ // w=p- q -> p,q
    if(w>0) q=lowbit( w),p=w+q;
    else    p=lowbit(-w),q=p-w;
}
int N[MAXN],M[MAXN];
void maintain1(){
    up(1,n,i) if(A[i]!=-2&&A[i]!=-1){
            int a=i,b=N[a],c=N[b];
            if(F[a]!=F[b]&&F[a]==F[c]){
                find_add(D[0]-D[b]-A[a]-1,A[c],A[b]);
            }
        }
}
void maintain2(){
    up(1,n,i){
        if(A[i]==-1) O[i]=114514+i; else O[i]=countz(A[i])/2;
    }
}
void showshowway(){
    up(1,n,i){
        printf("%lld, %d\n",A[i],F[i]);
    }
}
bool solve2(){
    up(1,n,i) N[i]=i+1; N[n]=1;
    up(1,n,i) M[i]=i-1; M[1]=n;
    up(2,n,i){
        if(  D[0]-D[i]        ==1) F[i+1]= F[i  ]; else
        if(((D[0]-D[i])%3+3)%3==1) F[i+1]=!F[i-1]; else
        if(((D[0]-D[i])%3+3)%3==2) F[i+1]=!F[i  ];
    }
    F[0]=F[n],F[n+1]=F[1]; bool g=0,h=0;
    up(1,n,i) g|=F[i],h|=F[i]==F[i+1];
    if(g==0){
        up(1,n,i) A[i]=-1; maintain2(); return check();
    } else if(h==0){
        i64 w=D[0]-D[1]-1;
        up(0,20,i){
            bool f=false;
            up(0,20,j){
                up(0,20,k){
                    i64 a=1ll<<(j<<1);
                    i64 b=1ll<<(i<<1);
                    i64 c=1ll<<(k<<1);
                    if(w==a+2*b+c) f=true;
                }
            }
            if(f){
                A[1]=(1ll<<(i<<1));
                maintain1(),maintain1(),maintain2();
                if(check()) return true;
            }
        }
    } else {
        up(1,n,i) if(F[i]!=F[M[i]]){
            int l=i,r=i,p=i;
            for(;F[r]==F[N[r]];r=N[r]); if(l==r) continue;
            find_min(D[0]-D[r]-1,A[r],A[M[r]]);
            find_min(D[0]-D[l]-1,A[l],A[N[l]]);
            for(;F[p]==F[N[p]];p=N[p]) if(A[p]==-2) A[p]=-1;
            if(l<r) i=r; else break;
        }
        maintain1(),maintain1(),maintain2();
        return check();
    }
    return 0;
}
void solve1(bool f){
    up(1,n,i) A[i]=-2,F[i]=-1; F[1]=false,F[2]=f;
    if(solve2()){
        up(1,n,i) printf("%d %d\n",F[i],O[i]); exit(0);
    }
}
i64 p1,p2,q1,q2;
int main(){
    n=qread(); up(0,n,i) D[i]=qread();
    if(n==1){puts("1");exit(0);}
    F[1]=false;
    if(  D[0]-D[1]        ==1)              solve1(false); else
    if(((D[0]-D[1])%3+3)%3==1) solve1(true),solve1(false); else
    if(((D[0]-D[1])%3+3)%3==2) solve1(true)              ;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值