【JZOJ5284】 超级翻转

超级翻转

(File IO)input:turn.in output:turn.out
Description
Description
Description1
Input
Input
Output
Output

Sample Input

3
1
1
1 1
2
0 1
1 0
2 2
7
1 1 0 0 1 0 1
1 1 0 1 0 1 1
1 0 1 0 1 1 1
0 1 0 1 1 0 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1

Sample Output

R
ULDR
No Solution!

Data constraint
Data



线性基引入

对于线性基,我有这样的理解:
线性基一般是用来处理异或的问题的

性质

对于一个数组a,有一个对应线性基l
它有以下性质:
1. a中选任意数的异或都可以由l中选任意数异或成
2. l中任意数的异或不会与另一些数的异或相同
3. l包含 log(max{ai}) 个二进制数,第i个二进制有i位,最高位为i
4. 若线性基中的1~k位都有数,那么它一定可以异或成 [1,2k1] 的任意数
5. 线性基中取任意低位的数去异或高位的数,线性基的以上性质不变

操作

插入
将一个数v插入线性基,对v按高位到低位枚举,对于v的第i位v[i]=1若 l[i]=nil 则将v放入l[i],否则让 v=vxorl[i]
v是否能成功插入取决于v是否不能被线性基构成,若能被构成,最后v=0,否则 v0 且v放入了某个l[i]
同时v的最后取值能判断v是否能被线性基构成

    //插入
    bool insert(long long val){
        for(int i=60;i>=0;i--)
            if(val>>i){
                if(!a[i])){a[i]=val;break;}
                val^=a[i];
            }
        return val;
    }
    //判断某数是否在线性基里
    bool cover(long long val){
        for(int i=60;i>=0;i--)
            if(val>>i){
                if(!a[i]){return 0;}
                val^=a[i];
            }
        return val==0;
    }

合并
将线性基b合并到线性基a
即将b的每一位都插入到a就好了

void merge(Linear_Basis &a,Linear_Basis b){
    for(int i=62;i+1;i--)a.insert(b.a[i]);
}

求线性基最大能构成的数
枚举线性基每位,将该位的数异或当前答案去更新答案

long long max(){
        long long ans=0;
        for(int i=62;i+1;i--)
            if(a[i]^ans>ans)ans^=a[i];
        return ans;
    }

求第k小能组成的数
根据性质5,我们可以将线性基内部的元素重构:
使得对于第i位为1的数只能出现在线性基的第i个数
那么就可以根据k的2进制去求第k小

void rebuild(){
        for(int i=62;i+1;i--)for(int j=i-1;j+1;j--)if(a[i]&1LL<<j)a[i]^=a[j];
        for(int i=0;i<63;i++)if(a[i])p[cnt++]=a[i];
    }
    long long kth(long long k){
        if(cnt<=1LL<<k)return -1;
        long long ans=0;
        for(int i=0;i<cnt;i++)if(k&1LL<<i)ans^=p[i];
        return ans;
    }

All:

struct Linear_Basis{
    long long a[63],p[63];
    int cnt;
    void clear(){
        for(int i=0;i<63;i++)a[i]=p[i]=0;cnt=0;
    }
    bool insert(long long val){
        for(int i=62;i+1;i--)
            if(val>>i&1){
                if(!a[i]){a[i]=val;return 0;}
                val^=a[i];
            }return 1;
    }
    bool cover(long long val){
        for(int i=62;i+1;i--)
            if(val>>i&1){
                if(!a[i])return 0;
                val^=a[i];
            }return 1;
    }
    long long max(){
        long long ans=0;
        for(int i=62;i+1;i--)
            if(a[i]^ans>ans)ans^=a[i];
        return ans;
    }
    void rebuild(){
        for(int i=62;i+1;i--)for(int j=i-1;j+1;j--)if(a[i]&1LL<<j)a[i]^=a[j];
        for(int i=0;i<63;i++)if(a[i])p[cnt++]=a[i];
    }
    long long kth(long long k){
        if(cnt<=1LL<<k)return -1;
        long long ans=0;
        for(int i=0;i<cnt;i++)if(k&1LL<<i)ans^=p[i];
        return ans;
    }
};
void merge(Linear_Basis &a,Linear_Basis b){
    for(int i=62;i+1;i--)a.insert(b.a[i]);
}


解题思路

枚举终点,翻转一下路径周围,我们有以下发现:
Solution

那么我们将每一个格子翻转的影响放入线性基,并记录某状态可以由那些格子影响构成(再开一个bitset储存),那么枚举终点时只用判断改状态是否在线性基里,取出构成状态,求答案就好了

怎么求答案??很简单输出枚举的路,在枚举每个影响到的点,输出路径,绕一圈,输出,原路返回到枚举的终点,输出
细节有一点点多

code:

#include<cstring>
#include<cstdio>
#include<cctype>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<bitset>

using namespace std;

typedef bitset<225> BS;

struct lb{
    BS d,p;
    void clearmem(){
        for(int i=0;i<225;i++)p[i]=0;
    }
    void clearall(){
        for(int i=0;i<225;i++)d[i]=p[i]=0;
    }
};

lb state[226],temp;
int n,T,path[226],Tot;

struct LB{
    lb a[225];
    bool insert(lb val){
        for(int i=224;i>=0;i--)
            if(val.d[i]){
                if(a[i].d.none()){a[i]=val;break;}
                val.d^=a[i].d;val.p^=a[i].p;
            }
        return val.d.any();
    }
    bool cover(lb val){
        temp.clearmem();
        for(int i=224;i>=0;i--)
            if(val.d[i]){
                if(a[i].d.none()){break;}
                val.d^=a[i].d;temp.p^=a[i].p;
            }
        return val.d.none();
    }
    void clear(){
        for(int i=0;i<225;i++)a[i].clearall();
    }
}all;

bool find(int x,int y,int &tx,int &ty,int d){
    if(all.cover(state[0])){state[0].p=temp.p;tx=x;ty=y;return 1;}else state[0].clearmem();
    if(d<=1 && x>1){
        if(y>1)state[0].d.flip((x-2)*n+y-2);if(y<=n)state[0].d.flip((x-2)*n+y-1);
        if(find(x-1,y,tx,ty,1)){path[++Tot]=1;return 1;}
        if(y>1)state[0].d.flip((x-2)*n+y-2);if(y<=n)state[0].d.flip((x-2)*n+y-1);
    }
    if((d==0 || d==2)&& x<=n){
        if(y>1)state[0].d.flip((x-1)*n+y-2);if(y<=n)state[0].d.flip((x-1)*n+y-1);
        if(find(x+1,y,tx,ty,2)){path[++Tot]=2;return 1;}
        if(y>1)state[0].d.flip((x-1)*n+y-2);if(y<=n)state[0].d.flip((x-1)*n+y-1);
    }
    if(d!=4 && y<=n){
        if(x>1)state[0].d.flip((x-2)*n+y-1);if(x<=n)state[0].d.flip((x-1)*n+y-1);
        if(find(x,y+1,tx,ty,3)){path[++Tot]=3;return 1;}
        if(x>1)state[0].d.flip((x-2)*n+y-1);if(x<=n)state[0].d.flip((x-1)*n+y-1);
    }
    if(d!=3 && y>1){
        if(x>1)state[0].d.flip((x-2)*n+y-2);if(x<=n)state[0].d.flip((x-1)*n+y-2);
        if(find(x,y-1,tx,ty,4)){path[++Tot]=4;return 1;}
        if(x>1)state[0].d.flip((x-2)*n+y-2);if(x<=n)state[0].d.flip((x-1)*n+y-2);
    }
    return 0;
}

void write(int fx,int fy,int tx,int ty){
    for(int i=fx;i<tx;i++)printf("D");
    for(int i=fy;i<ty;i++)printf("R");
    for(int i=ty;i<fy;i++)printf("L");
    for(int i=tx;i<fx;i++)printf("U");
}

void walk(int fx,int fy,int tx,int ty,int w){
    int x=-1,y=-1,W;
    for(int i=1,t=0;i<=n;i++){
        int p=0;
        for(int j=1;j<=n;j++,t++)if(t>w && state[0].p[t]){W=t,x=i,y=j;p=1;break;}
        if(p)break;
    }
    write(fx,fy,tx,ty);printf("RDLU");if(x>0 && y>0)walk(tx,ty,x,y,W);write(tx,ty,fx,fy);   
}

int main(){
    freopen("turn.in","r",stdin);
    freopen("turn.out","w",stdout);
scanf("%d",&T);
for(int I=T;I;I--){
if(I<T)printf("\n");
    int tot=0,x,y,tx,ty;Tot=0;
    all.clear();
    scanf("%d",&n);
    temp.clearall();for(int i=0;i<=n*n;i++)state[i].clearall();
    for(int k=0;k<n*n;k++)state[0].d[k]=state[0].p[k]=0;
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){
        int x;scanf("%d",&x);state[0].d[tot++]=x;
        for(int k=0;k<n*n;k++)state[tot].d[k]=state[tot].p[k]=0;
        if((tot-1)%n!=0)state[tot].d[tot-2]=1;
        if(tot%n!=0)state[tot].d[tot]=1;
        if(tot>n)state[tot].d[tot-n-1]=1;
        if(tot+n<=n*n)state[tot].d[tot+n-1]=1;
        state[tot].p[tot-1]=1;
        all.insert(state[tot]);
    }
    scanf("%d %d",&x,&y);
    if(!find(x,y,tx,ty,0)){printf("No Solution!");continue;}
    for(int i=Tot;i;i--){
        if(path[i]==1)putchar('U');
        if(path[i]==2)putchar('D');
        if(path[i]==3)putchar('R');
        if(path[i]==4)putchar('L');
    }
    if(state[0].p.none())continue;
    for(int i=1,t=0;i<=n;i++){
        int p=0;
        for(int j=1;j<=n;j++,t++)if(state[0].p[t]){walk(tx,ty,i,j,t);p=1;break;}
        if(p)break;
    }   
}
    fclose(stdin);fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值