loj3301.「联合省选 2020 A」魔法商店

题目链接

论文题,我连暴力都不会打qq_emoji: cy

这个异或显然是线性基,后面的部分如果大家知道的话就是保序回归的板子(参考高睿泉 2018 年集训队论文《浅谈保序回归问题》,可以去这里自取)。

保序回归问题的模型是给出一个点集和偏序关系,每个点有权值 \(w_i\),求一组 \(f_i\),使得如果 \(w_i<w_j\)\(f_i<f_j\),最小化 \(\sum_{i=1}^n|f_i-w_i|^p\)

偏序关系相当于一个 DAG,然后我们整体二分,二分每个点的取值。考虑每个点取左半段还是右半段更优,不难发现如果一个点选右半段,那么 DAG 上这个点能走到的点都必须选右半段,这就是个最小权闭合子图,网络流即可。

解决了保序回归之后考虑怎样建图。直接建出线性基,然后枚举所有元素,每次删掉一个,再枚举全部的物品,把它们插入线性基,如果能插入说明这个元素也可以成为集合中的一部分,如果是 \(A\) 集合就删去的物品向当前物品连边,否则反过来,这样就建出图了。

细节巨大多,写起来很像是个三合一题目(

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
#define int long long
struct edge
{
    int nxt,to,weight;
}e[1000001<<1];
int n,m,tot,h[1005],s,t,dep[1005],cur[1005],q[1005],tmp[1005][2],w[1005],ans[1005],a[65],b[65],sum;
unsigned long long p[64],c[1005];
bool vis[1005];
int num[1005];
vector<int> v[1005];
inline void init(int maxn)
{
    s=maxn+1;
    t=s+1;
    tot=1;
}
inline void add(int x,int y,int w)
{
    e[++tot].nxt=h[x];
    h[x]=tot;
    e[tot].to=y;
    e[tot].weight=w;
}
inline bool bfs()
{
    for(register int i=0;i<=t;++i)
    {
        cur[i]=h[i];
        dep[i]=1ll<<60;
        vis[i]=0;
    }
    queue<int> q;
    q.push(s);
    dep[s]=0;
    while(!q.empty())
    {
        int k=q.front();
        q.pop();
        vis[k]=0;
        for(register int i=h[k];i;i=e[i].nxt)
            if(e[i].weight&&dep[e[i].to]>dep[k]+1)
            {
                dep[e[i].to]=dep[k]+1;
                if(!vis[e[i].to])
                {
                    vis[e[i].to]=1;
                    q.push(e[i].to);
                }
            }
    }
    return dep[t]^dep[0];
}
int dfs(int k,int f)
{
    if(k==t)
        return f;
    int r=0,used=0;
    for(register int i=cur[k];i;i=e[i].nxt)
    {
        cur[k]=i;
        if(e[i].weight&&dep[e[i].to]==dep[k]+1)
            if((r=dfs(e[i].to,min(e[i].weight,f-used))))
            {
                e[i].weight-=r;
                e[i^1].weight+=r;
                used+=r;
                if(f==used)
                    break;
            }
    }
    return used;
}
inline void dinic()
{
    while(bfs())
        dfs(s,1ll<<60);
}
void solve(int l,int r,int ql,int qr)
{
    //printf("%lld %lld %lld %lld\n",l,r,ql,qr);
    if(ql>qr||l>r)
        return;
    if(l==r)
    {
        for(register int i=ql;i<=qr;++i)
            ans[q[i]]=l;
        return;
    }
    if(r-l==1)
    {
        init(qr-ql+1);
        for(register int i=ql;i<=qr;++i)
            num[q[i]]=i-ql+1;
        for(register int i=ql;i<=qr;++i)
        {
            if(((w[q[i]]-l)<<1)-1>0)
            {
                add(s,num[q[i]],((w[q[i]]-l)<<1)-1);
                add(num[q[i]],s,0);
            }
            else
            {
                add(num[q[i]],t,-(((w[q[i]]-l)<<1)-1));
                add(t,num[q[i]],0);
            }
            for(register int j=0;j<(int)v[q[i]].size();++j)
                if(num[v[q[i]][j]])
                {
                    add(num[q[i]],num[v[q[i]][j]],1ll<<60);
                    add(num[v[q[i]][j]],num[q[i]],0);
                }
        }
        dinic();
        for(register int i=ql;i<=qr;++i)
        {
            ans[q[i]]=dep[num[q[i]]]==1ll<<60? l:r;
            num[q[i]]=0;
        }
        for(register int i=1;i<=t;++i)
            h[i]=0;
        return;
    }
    int mid=(l+r)>>1;
    init(qr-ql+1);
    for(register int i=ql;i<=qr;++i)
        num[q[i]]=i-ql+1;
    for(register int i=ql;i<=qr;++i)
    {
        if(w[q[i]]>mid)
        {
            add(s,num[q[i]],(w[q[i]]-mid)<<1);
            add(num[q[i]],s,0);
        }
        else
        {
            add(num[q[i]],t,(mid-w[q[i]])<<1);
            add(t,num[q[i]],0);
        }
        for(register int j=0;j<(int)v[q[i]].size();++j)
            if(num[v[q[i]][j]])
            {
                add(num[q[i]],num[v[q[i]][j]],1ll<<60);
                add(num[v[q[i]][j]],num[q[i]],0);
            }
    }
    dinic();
    int cnt1=0,cnt2=0;
    for(register int i=ql;i<=qr;++i)
    {
        if(dep[num[q[i]]]==1ll<<60)
            tmp[++cnt1][0]=q[i];
        else
            tmp[++cnt2][1]=q[i];
        num[q[i]]=0;
    }
    for(register int i=1;i<=cnt1;++i)
        q[ql+i-1]=tmp[i][0];
    for(register int i=1;i<=cnt2;++i)
        q[ql+cnt1+i-1]=tmp[i][1];
    for(register int i=1;i<=t;++i)
        h[i]=0;
    solve(l,mid,ql,ql+cnt1-1);
    solve(mid,r,ql+cnt1,qr);
}
inline void clear()
{
    for(register int i=0;i<64;++i)
        p[i]=0;
}
inline int insert(unsigned long long x)
{
    for(register int i=63;~i;--i)
        if(x&(1ull<<i))
        {
            if(!p[i])
            {
                p[i]=x;
                return i;
            }
            else
                x^=p[i];
        }
    return -1;
}
signed main()
{
    scanf("%lld%lld",&n,&m);
    for(register int i=1;i<=n;++i)
        scanf("%llu",&c[i]);
    for(register int i=1;i<=n;++i)
        scanf("%lld",&w[i]);
    for(register int i=1;i<=m;++i)
        scanf("%lld",&a[i]);
    for(register int i=1;i<=m;++i)
        scanf("%lld",&b[i]);
    for(register int i=1;i<=m;++i)
    {
        clear();
        for(register int j=1;j<=m;++j)
            if(j!=i)
                insert(c[a[j]]);
        for(register int j=1;j<=n;++j)
            if(j!=a[i])
            {
                int pos=insert(c[j]);
                if(~pos)
                {
                    v[a[i]].push_back(j);
                    p[pos]=0;
                }
            }
    }
    for(register int i=1;i<=m;++i)
    {
        clear();
        for(register int j=1;j<=m;++j)
            if(j!=i)
                insert(c[b[j]]);
        for(register int j=1;j<=n;++j)
            if(j!=b[i])
            {
                int pos=insert(c[j]);
                if(~pos)
                {
                    v[j].push_back(b[i]);
                    p[pos]=0;
                }
            }
    }
    for(register int i=1;i<=n;++i)
        q[i]=i;
    solve(0,1e6,1,n);
    for(register int i=1;i<=n;++i)
        sum+=(ans[i]-w[i])*(ans[i]-w[i]);
    printf("%lld\n",sum);
    return 0;
}
posted @ 2021-08-19 21:42  绝顶我为峰  阅读(72)  评论(0)    收藏  举报