[AWTF 2019] Triangular Lamps Hard
[AWTF 2019] Triangular Lamps Hard
首先考虑将所有亮着的灯转移到同一行。
不妨令这一行的纵坐标为 \(V = -\infty\),考虑将一盏灯往下转移时的情况。
不妨考虑从上往下,一行一行转移,每次将亮着的灯作为三角形的上端顶点操作。
下面我们描述操作的过程,\(0\) 为灭,\(1\) 为亮:
-
\(0\) 次转移:\([1]\)
-
\(1\) 次转移:\([1,1]\)
-
\(2\) 次转移:\([1,0,1]\)
-
\(3\) 次转移:\([1,1,1,1]\)
-
\(4\) 次转移:\([1,0,0,0,1]\)
-
\(5\) 次转移:\([1,1,0,0,1,1]\)
我们发现,从 \((x,y)\) 出发,\((x-a,y+b)\) 的系数是 \(\dbinom{a}{b} \bmod 2\)。
显然,后者等价于 \(b \subseteq a\),而所有点对应的 \(a\) 在数据下都是可以计算的。
也就是说,我们可以 \(O(n)\) check 一个灯是否被点亮。
我们找到第 \(V\) 行最左边点亮的灯和最右边点亮的灯,通过这两个灯的位置即可求出答案。
具体地,假设坐标为 \((L,V)\) 和 \((R,V)\),答案为 \((L,V+R-L)\)。
接下来我们的任务是算出 \(L\) 和 \(R\),不妨继续观察性质。
将 \((x,y)\) 按照 \((x-y) \bmod 3\) 意义分类,则一次操作改动的三个点各不相同。
考虑 \((x-y) \equiv 0,1,2 \pmod 3\) 的 \((x,y)\) 个数奇偶性。
根据操作流程,这些点中必然存在一类的个数为奇数。
考虑找到一个奇数个数的类,接下来我们尝试找到该类中的任意一个亮着的点。
对这个问题尝试二分,显然 \(2k\) 只能分成 \(2p\) 和 \(2q+1\),每次取奇数的一边即可。
现在考虑如何求一段区间内模 \(3\) 为 \(r\) 且亮着的点的奇偶性。
这种模 \(2\) 意义下的问题,我们拆到原先的每个点上进行数位 dp,复杂度 \(O(n \log V)\)。
加上二分,这一步的时间复杂度是 \(O(n \log^2 V)\)。
接下来考虑找到一个点后该如何处理,考虑据此找到 L 和 R。
根据题目的性质,我们可以倍增,从大到小考虑 \(2^i\),判断横坐标是否能加减 \(2^i\)。
显然在组合数性质下,这个方法是正确的,这一步的时间复杂度 \(O(n \log V)\)。
时间复杂度 \(O(n \log^2 V)\)。
//Ad astra per aspera
#include<iostream>
#include<cstdio>
using namespace std;
const int C=60;
const long long V=-100000000000000000;
const long long L=-100000000000000000;
const long long R=300000000000000000;
int n;
long long x[10010],y[10010];
struct Data{
bool ans[3];
}O,dta[C+10];
Data operator ^(const Data &lhs,const Data &rhs){
Data hs;
hs.ans[0]=lhs.ans[0]^rhs.ans[0];
hs.ans[1]=lhs.ans[1]^rhs.ans[1];
hs.ans[2]=lhs.ans[2]^rhs.ans[2];
return hs;
}
Data qry(long long num,long long pos){
Data ans=O,pre=O;
pre.ans[0]=true;
for(int i=0;i<=C;i++){
dta[i]=pre;
if(num&(1ll<<i)){
Data tmp;
if(i%2==0){
tmp.ans[0]=pre.ans[2];
tmp.ans[1]=pre.ans[0];
tmp.ans[2]=pre.ans[1];
}
if(i%2==1){
tmp.ans[0]=pre.ans[1];
tmp.ans[1]=pre.ans[2];
tmp.ans[2]=pre.ans[0];
}
pre=pre^tmp;
}
}
long long sum=0;
int pret=0;
for(int i=C;i>=0;i--){
if(pos&(1ll<<i)){
if((sum&num)==sum){
Data tmp;
if(pret==0){
tmp.ans[0]=dta[i].ans[0];
tmp.ans[1]=dta[i].ans[1];
tmp.ans[2]=dta[i].ans[2];
}
if(pret==1){
tmp.ans[0]=dta[i].ans[2];
tmp.ans[1]=dta[i].ans[0];
tmp.ans[2]=dta[i].ans[1];
}
if(pret==2){
tmp.ans[0]=dta[i].ans[1];
tmp.ans[1]=dta[i].ans[2];
tmp.ans[2]=dta[i].ans[0];
}
ans=ans^tmp;
}
sum+=1ll<<i;
if(i%2==0){
pret=(pret+1)%3;
}
else{
pret=(pret+2)%3;
}
}
}
return ans;
}
int mod_3(long long num){
return (num%3+3)%3;
}
Data query(long long l,long long r){
Data ans=O;
for(int i=1;i<=n;i++){
long long pre_l=x[i],pre_r=x[i]+(y[i]-V);
long long now_l=max(l,pre_l),now_r=min(r,pre_r);
if(now_l<=now_r){
Data tmp=O,tmp2;
tmp=tmp^qry(y[i]-V,now_l-pre_l);
tmp=tmp^qry(y[i]-V,(now_r+1)-pre_l);
if(mod_3(pre_l)==0){
tmp2.ans[0]=tmp.ans[0];
tmp2.ans[1]=tmp.ans[1];
tmp2.ans[2]=tmp.ans[2];
}
if(mod_3(pre_l)==1){
tmp2.ans[0]=tmp.ans[2];
tmp2.ans[1]=tmp.ans[0];
tmp2.ans[2]=tmp.ans[1];
}
if(mod_3(pre_l)==2){
tmp2.ans[0]=tmp.ans[1];
tmp2.ans[1]=tmp.ans[2];
tmp2.ans[2]=tmp.ans[0];
}
ans=ans^tmp2;
}
}
return ans;
}
long long div_2(long long num){
if(num%2==0 || num>=0){
return num/2;
}
else{
return num/2-1;
}
}
bool check(Data hs){
return hs.ans[0] || hs.ans[1] || hs.ans[2];
}
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld %lld",&x[i],&y[i]);
}
long long l=L,r=R;
Data tmp=query(l,r);
int pre;
if(tmp.ans[0]){
pre=0;
}
if(tmp.ans[1]){
pre=1;
}
if(tmp.ans[2]){
pre=2;
}
while(l<r){
long long mid=div_2(l+r);
if(query(l,mid).ans[pre]){
r=mid;
}
else{
l=mid+1;
}
}
for(int i=C;i>=0;i--){
if(check(query(l-(1ll<<i),l-(1ll<<i)))){
l-=(1ll<<i);
}
if(check(query(r+(1ll<<i),r+(1ll<<i)))){
r+=(1ll<<i);
}
}
printf("%lld %lld",l,V+r-l);
return 0;
}

浙公网安备 33010602011771号