题目链接

首先我们先考虑一维,并且坐标全都非负,有一个点在 $0$ 的情况。

可以将每次的操作抽象成乘上一个多项式 $(1+x^p)$,然后系数在 $\bmod 2$ 意义下运算,要求等于最终的多项式。(所以操作顺序其实是没关系的)

那么我们每次只需要找到一个能除的多项式尽量除就好了。具体地,我们每次找到距离 $1$ 最近的位置 $p$,然后除掉 $(1+x^p)$ 即可。

如果有负数怎么办?发现这样会比较难维护这个带负数次数的多项式,但是我们很会做起点是 $0$ 的情况,所以我们尝试平移这些点。首先有一个结论是:只有长度绝对值形如 $x,2x$ 这样的向量会互相影响抵消一些点,其余的是不会影响的。(这个结论我并不会证明)

那么我们考虑原状态的一种方案,我们可以把所有往左的边反向,这样对最终方案的影响就是整体平移,最终将最左边的点平移到了 $0$ 位置。设最左边的点原位置是 $p$,那么我们就可以先假设最左边的点在 $0$ 位置做一遍暴力除,然后现在问题就是如何将这个方案平移回去:这是个背包问题。现在任务就是要选择一些向量将它们反向来让这个最左边的点到达 $p$ 位置。

首先根据上面的结论,如果一个向量长度 $x \bmod 2 = 0$,我们可以把它拆成两个 $\frac{x}{2}$。这样拆到不能再拆的时候,就能让这个方案尽可能能找出一组合法的反转解。我们再找出一个子集满足和为 $-p$,将这里面的边都反转一下,就可以了。

现在考虑如何扩展到二维的情况:首先一维的问题启发负数次数并不是很好维护,所以我们还是先整体平移,我们先将所有点都平移到第一象限内。(实际上我们只需要平移一个 $(x,y)$ 这个二元组最小的点到 $(0,0)$ 就好了,因为这个凸包如果合法一定是对称的)。

有一个结论是:我们最终使用的所有向量的闵可夫斯基和的凸包和输入的点的凸包同构。证明只需要考虑这些向量构成的闵可夫斯基和凸包上的点一定只能被一种组合表出(因为在边界上,也就是对应的向量必须取到最大值,所以这些点一定都要被出现过)。

所以我们考虑能否逆推出原来的向量:先对所有点建立凸包,然后考虑这个凸包上的每条边。现在有个麻烦的地方凸包的边怎么处理:这一条边上可能会有很多个点,但我们发现凸包这条边上的所有点,在不考虑这条边的方向上的向量上的时候,其他方向向量的选取方案都是唯一的。所以我们只需要决定这条边方向上的向量如何选,所以「这个方向」上的问题就变成了一个一维的问题,这样做完之后就能求出所有方向上的向量了。换一句话说,这个结论等价于我们每次除的多项式一定是在凸包的边上的。

最后我们要处理平移的问题,相当于还是选出一个子集,满足和是 $(-x_0,-y_0)$,将这个子集的边反向。用个二维的背包即可。

#include <bits/stdc++.h>

#define fi first
#define se second
#define DB double
#define U unsigned
#define LL long long
#define LD long double
#define pb emplace_back
#define pf emplace_front
#define MP std::make_pair
#define SZ(x) ((int)x.size())
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 300 + 5;
const int zero = 300 + 2;

struct P{
    int x,y;
    P(int x=0,int y=0) : x(x),y(y) {}

    P operator + (const P &t) const {return P(x+t.x,y+t.y);}
    P operator - (const P &t) const {return P(x-t.x,y-t.y);}
    P operator * (const int &t) const {return P(x*t,y*t);}
    P operator / (const int &t) const {return P(x/t,y/t);}
    int det(const P &t) const {return x*t.y-y*t.x;}
    int dot(const P &t) const {return x*t.x+y*t.y;}

    inline bool operator < (const P &t) const {
        if(x == t.x) return y < t.y;
        return x < t.x;
    }
};

inline int cross(P a,P b,P c){
    return (b-a).det(c-a);
}

class SplittingFoxes4{
private:
    P a[505];int n;
    int st[595],tp;
    std::vector<P> line[585];
    std::vector<P> ans;
    std::vector<int> vec;
    bool now[MAXN*3];

    inline bool gao(int v){
        P e = line[v][1]-line[v][0];int g = std::__gcd(e.x,e.y);
        if(g < 0) g = -g;
        e.x /= g;e.y /= g;vec.clear();
        FOR(i,1,SZ(line[v])-1) vec.pb(((line[v][i]-line[v][0]).dot(e))/(e.dot(e)));
        CLR(now,0);
        int len = vec.back();for(auto x:vec) now[x] = 1;now[0] = 1;
        while(len){
            int ps = -1;
            FOR(i,1,len) if(now[i]){ps = i;break;}
            if(ps == -1) break;
            FOR(i,ps,len) now[i] ^= now[i-ps];
            FOR(i,len-ps+1,len) if(now[i]) return 0;
            len -= ps;int cnt = 1;
            while(!(ps&1)) ps >>= 1,cnt <<= 1;
            FOR(i,1,cnt) ans.pb(e*ps);
        }
        return 1;
    }

    std::bitset<MAXN*2> f[MAXN],dp[MAXN][MAXN];

public:
    inline std::vector<int> construct(std::vector<int> x,std::vector<int> y){
        n = SZ(x);FOR(i,1,n) a[i] = P(x[i-1],y[i-1]);
        std::sort(a+1,a+n+1);
        if(a[1].x > 0) return {-1};
        FOR(i,1,n){
            while(tp >= 2 && cross(a[st[tp-1]],a[st[tp]],a[i]) <= 0){
                if(cross(a[st[tp-1]],a[st[tp]],a[i]) == 0) line[i].insert(line[i].end(),all(line[st[tp]]));
                --tp;
            }
            line[i].pb(a[i]);st[++tp] = i;
        }
        FOR(i,2,tp){
            line[st[i]].insert(line[st[i]].begin(),{a[st[i-1]]});
            if(!gao(st[i])) return {-1};
        }
//        for(auto x:ans) DEBUG(x.x),DEBUG(x.y);
        f[0].set(zero);
        for(auto v:ans){
            ROF(j,MAXN-1,v.x){
                if(v.y >= 0) f[j] |= f[j-v.x]<<v.y;
                else f[j] |= f[j-v.x]>>(-v.y);
            }
        }
        FOR(i,1,n) if(!f[(a[i]-a[1]).x][(a[i]-a[1]).y+zero]) return {-1};
        dp[0][0].set(zero);
        FOR(i,1,SZ(ans)){
            P v = ans[i-1];
            FOR(j,0,MAXN-1) dp[i][j] = dp[i-1][j];
            FOR(j,v.x,MAXN-1){
                if(v.y >= 0) dp[i][j] |= dp[i-1][j-v.x]<<v.y;
                else dp[i][j] |= dp[i-1][j-v.x]>>(-v.y);
            }
        }
        int nowx = -a[1].x,nowy = -a[1].y;

        if(!dp[SZ(ans)][nowx][nowy+zero]) return {-1};
        ROF(i,SZ(ans),1){
            P v = ans[i-1];
            if(nowx-v.x >= 0 && nowy+zero-v.y >= 0 && dp[i-1][nowx-v.x][nowy+zero-v.y]){
                nowx -= v.x;nowy -= v.y;
                ans[i-1] = ans[i-1]*-1;
            }
        }
        std::vector<int> out;
        for(auto v:ans) out.pb(v.x),out.pb(v.y);
        return out;
    }
};
Last modification:July 3rd, 2021 at 11:25 pm
如果觉得我的文章对你有用,请随意赞赏