构造好题


CF1270G Subset with Zero Sum

这是一道很好的图论构造题。

题目中的 \(i-n\le a_i\le i-1\) 经过调换改为 \(1\le i-a_i\le n\),可以发现 \(\sum a_i=0\)\(\sum i=\sum i-a_i\) 等价。此时便可以从 \(i\)\(i-a_i\) 连一条边,由于是 \(n\) 个点,\(n\) 条边的图,因此至少有一个环。对这个环上的节点编号求和,便会得到之前的等式,因此就找见了一个满足的解。

此题的妙处在于通过确定 \(a_i\) 的范围想到连边,再想到是通过求和的方式转换为找环。

code
#include
using namespace std;
const int N=1e6+5;
int t;
int n,a[N],to[N],flag[N],d[N],u,cnt;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            to[i]=i-a[i];
            flag[i]=0;
        }
        for(int i=1;i<=n;i++)
        {
            u=i,cnt=0;
            while(u&&!flag[u])
            {
                flag[u]=1;
                d[to[u]]=d[u]+1;
                u=to[u];
            }
            if(u)
            {
                printf("%d\n%d ",d[u]-d[to[u]]+1,u);
                break;
            }
        }
        flag[u]=0;
        u=to[u];
        while(flag[u]) printf("%d ",u),u=to[u];
        printf("\n");
    }
    return 0;
}

CF1427E Xum

这是一道很好的数学构造题。

首先容易发现一个偶数与它后面的那个奇数异或和为 \(1\),那么只需要找到这样的两个数即可。

设其中的偶数为 \(m\),奇数为 \(n\),则只需 \(n-m=1\),思考如何与 \(x\) 产生联系。

不妨设 \(n\)\(x\) 的倍数,\(m\)\(y\) 的倍数,即转为 \(ax-by=1\),成为了扩展欧几里德的模版,由裴蜀定理得,对于一对 \(x,y\),当且仅当 \((x,y)=1\) 时,必能找到一对正数解 \(a,b\),并且 \(b\) 为偶数。

问题转化为找到一个数 \(y\),使得 \((x,y)=1\)

一个神奇的操作:设 \(2^e\le x<2^{e+1}\),那么令 \(y=2^ex\oplus x=2^ex+x-2\times 2^e=(2^e+1)x-2^{e+1}\),此时 \((x,y)=(x,(2^e+1)x-2^{e+1})=(x,2^{e+1})=1\),满足要求,并且可以通过和与异或的方式实现。

code
#include
using namespace std;
#define int long long
int x,y;
int e,f,g;
int u,v;
int xt,yt;
int xf,yg;
int c[1000005][3],cnt;
int lowbit(int a)
{
    return a&(-a);
}
void add(int a,int b,int opt)
{
    c[++cnt][0]=a;
    c[cnt][1]=b;
    c[cnt][2]=opt;
}
void print(int a,int b,int opt)
{
    if(opt==0) printf("%lld + %lld\n",a,b);
    else printf("%lld ^ %lld\n",a,b);
    return;
}
void exgcd(int a,int b)
{
    if(b==0)
    {
        xt=1;
        yt=0;
        return;
    }
    exgcd(b,a%b);
    int z=xt;
    xt=yt;
    yt=z-a/b*yt;
    return;
}
void mul(int a,int b)
{
    u=lowbit(a);
    int sum=u*b;
    a-=u;
    for(;a;a-=lowbit(a))
    {
        u=lowbit(a);
        add(sum,u*b,0);
        sum+=u*b;
    }
}
signed main()
{
    scanf("%lld",&x);
    e=log2(x),y=x;
    for(int i=1;i<=e;i++) add(y,y,0),y<<=1;
    xf=y;
    add(x,y,1),y^=x;
    yg=y;
    exgcd(x,y);
    yt=-yt;
    while(xt<0||yt<0||yt%2==1) xt+=y,yt+=x;
    while(xt>2*y&&yt>2*x) xt-=2*y,yt-=2*x;
    f=log2(xt),g=log2(yt);
    for(int i=e+1;i<=f;i++) add(xf,xf,0),xf<<=1;
    for(int i=1;i<=g;i++) add(yg,yg,0),yg<<=1;
    mul(xt,x),mul(yt,y);
    add(xt*x,yt*y,1);
    printf("%lld\n",cnt);
    for(int i=1;i<=cnt;i++) print(c[i][0],c[i][1],c[i][2]);
    return 0;
}