【JZOJ 5284】 超级翻转

Description

这里写图片描述
这里写图片描述
1<=n<=15,1<=数据组数<=5

Analysis

这种题,发现数据范围暴力过不了,就应该发掘一下移动的性质

  • 显然一条边走的次数可以简化成0/1
  • 一个格子的状态取决于其相邻四条边走的次数异或

这样可以对每个格子列出方程,不过并不知道这个idea可不可做
继续发掘

  • 如果一个点四周被绕了一圈,那么造成的影响仅是这个格子四周的格子反色

并且,由于我们可以从起点走到一个点绕一圈再原路反回来,所以走原路返回的时候经过的那些边相当于不计影响
这就是个很好的性质了,不论起点在哪,我们都可以任意选定格子,造成的贡献是四周的格子反色
这就有点像这题
不过也可能不绕圈啊!!!
TM 就GG没关系,我们枚举一个终点
那么起点到终点必定会有一条路径,这条路径是什么?
在路径上绕一个圈试试?
路径刚好进行了偏移,归纳一发,我们可以通过选择点绕圈来更改路径(感性)
那么暴力走一遍,暴力反色,就变成上面那个题了
然后应该就能过了,复杂度应该是 n32n ,实际跑得挺快

你以为这样就完了?

一个巨大的优化

这里写图片描述
选红勾的格子等价于选蓝勾的格子
我们不需要枚举第一行的状态
存在一种可行解,第一行全不选
复杂度变成 n3

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
const int N=20;
int n,xs,ys,xt,yt,a[N][N],b[N][N],c[N][N];
//c:a matrix,value 1/0 standing node(i,j) is choosen or not
void go(int xs,int ys,int xt,int yt)
{
    int fx=xs<xt?1:-1;
    for(;xs!=xt;xs+=fx)
        if(fx==1) b[xs][ys-1]^=1,b[xs][ys]^=1;
        else b[xs-1][ys-1]^=1,b[xs-1][ys]^=1;
    fx=ys<yt?1:-1;
    for(;ys!=yt;ys+=fx)
        if(fx==1) b[xs-1][ys]^=1,b[xs][ys]^=1;
        else b[xs-1][ys-1]^=1,b[xs][ys-1]^=1;
    fo(i,0,n+1) b[i][0]=b[0][i]=b[i][n+1]=b[n+1][i]=0;
}
void output(int xs,int ys,int xt,int yt,int p)
{
    int fx;
    if(!p)
    {
        fx=xs<xt?1:-1;
        for(;xs!=xt;xs+=fx) putchar(fx==1?'D':'U');
        fx=ys<yt?1:-1;
        for(;ys!=yt;ys+=fx) putchar(fx==1?'R':'L');
    }
    else
    {
        fx=ys<yt?1:-1;
        for(;ys!=yt;ys+=fx) putchar(fx==1?'R':'L');
        fx=xs<xt?1:-1;
        for(;xs!=xt;xs+=fx) putchar(fx==1?'D':'U');
    }
}
int main()
{
    freopen("turn.in","r",stdin);
    freopen("turn.out","w",stdout);
    int T;
    for(scanf("%d",&T);T;T--)
    {
        scanf("%d",&n);
        fo(i,1,n)
            fo(j,1,n) scanf("%d",&a[i][j]);
        scanf("%d %d",&xs,&ys);
        bool p=0;
        for(xt=1;xt<=n+1;xt++)
        {
            for(yt=1;yt<=n+1;yt++)
            {
                memcpy(b,a,sizeof(a));
                go(xs,ys,xt,yt);
                mset(c,0);
                fo(i,2,n)//it can be proved that the first line of c can be all 0
                    fo(j,1,n)
                        c[i][j]=c[i-1][j-1]^c[i-2][j]^c[i-1][j+1]^b[i-1][j];
                bool ok=1;
                fo(i,1,n)
                    if(c[n][i-1]^c[n-1][i]^c[n][i+1]^b[n][i]) {ok=0;break;}
                if(!ok) continue;
                p=1;break;
            }
            if(p) break;
        }
        if(!p) {puts("No Solution!");continue;}
        fo(i,1,n)
            fo(j,1,n)
                if(c[i][j])
                {
                    output(xs,ys,i,j,0);
                    printf("RDLU");
                    output(i,j,xs,ys,1);
                }
        output(xs,ys,xt,yt,0);printf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值