• [省选联考 2020 A 卷] 魔法商店


    一、题目

    点此看题

    二、解法

    由于 这东西 已经鸽掉了,那么我就写一篇只记录做法的博客吧。

    首先讲一下保序回归的一般做法,我们考虑使用整体二分求解 \(f\)\(f\) 指调整后的价格),设现在 \(f\) 的范围是 \([l,r]\),我们要检测 \([f_i\leq mid]\) 是否为真,称额外限制 \(f_i\in[a,b]\) 的问题为 \(\{a,b\}\) 问题

    我们可以考虑原问题的 \(\{mid,mid+1\}\) 问题,\(f_i\) 是取 \(mid/mid+1\),如果 \(f_i\)\(mid\) 说明最终的 \(f_i\leq mid\),如果 \(f_i\)\(mid+1\) 说明最终的 \(f_i>mid\)

    这显然是最大权闭合子图模型,我们先强制所有点选 \(mid\),那么可以计算出调整至 \(mid+1\) 的贡献 \(c\)(但是建图的时候要用 \(-c\),最小转最大),用偏序关系建好图之后跑网络流,如果一个点不和源点联通那么选的是 \(mid\),否则选的是 \(mid+1\),可以根据这个来划分左右。


    本题还需要写出偏序关系,考虑最小的充要条件是:线性基的权值小于等于替换一个元素的线性基的权值,这显然是必要条件,可以通过调整法证明其充分性(张成线性空间不变,权值变大)

    那么暴力尝试替换就可以得到偏序关系,网络流的题就不说复杂度了

    #include <cstdio>
    #include <vector>
    #include <iostream>
    #include <queue>
    using namespace std;
    const int M = 1005;
    #define int long long
    const int inf = 1e18;
    #define pb push_back
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,v[M],a[M],b[M],c[M],xs[M];vector<int> g[M];
    int tot,S,T,d[M],f[M],cur[M],p[M],q[M],dp[M],L[M],R[M];
    struct edge{int v,c,next;}e[M*M];
    //line dick
    int ins(int x)
    {
    	for(int i=63;i>=0;i--)
    	{
    		if(!(x>>i&1)) continue;
    		if(xs[i]) x^=xs[i];
    		else {xs[i]=x;return i;}
    	}
    	return -1;
    }
    //network flow
    void init(int n)
    {
    	S=0;T=n+1;tot=1;
    	for(int i=0;i<=T;i++) f[i]=0;
    }
    void add(int u,int v,int c)
    {
    	e[++tot]=edge{v,c,f[u]},f[u]=tot;
    	e[++tot]=edge{u,0,f[v]},f[v]=tot;
    }
    int bfs()
    {
    	for(int i=S;i<=T;i++) d[i]=0;
    	queue<int> q;d[S]=1;q.push(S);
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();
    		if(u==T) return 1;
    		for(int i=f[u];i;i=e[i].next)
    		{
    			int v=e[i].v;
    			if(!d[v] && e[i].c>0)
    			{
    				d[v]=d[u]+1;
    				q.push(v);
    			}
    		}
    	}
    	return 0;
    }
    int dfs(int u,int ept)
    {
    	if(u==T) return ept;
    	int flow=0,tmp=0;
    	for(int &i=cur[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(d[v]==d[u]+1 && e[i].c>0)
    		{
    			tmp=dfs(v,min(ept,e[i].c));
    			if(!tmp) continue;
    			flow+=tmp;ept-=tmp;
    			e[i].c-=tmp;e[i^1].c+=tmp;
    			if(!ept) break;
    		}
    	}
    	return flow;
    }
    //divide and conquer
    int calc(int x,int y) {return (v[x]-y)*(v[x]-y);}
    void cdq(int ql,int qr,int l,int r)
    {
    	if(ql>qr) return ;//no element
    	if(l==r)
    	{
    		for(int i=ql;i<=qr;i++) dp[p[i]]=l;
    		return ;
    	}
    	int mid=(l+r)>>1,tl=0,tr=0;
    	for(int i=ql;i<=qr;i++) q[p[i]]=i-ql+1;
    	init(qr-ql+1);
    	for(int i=ql;i<=qr;i++)
    	{
    		int u=p[i],t=calc(u,mid)-calc(u,mid+1);
    		if(t>0) add(S,q[u],t);
    		else add(q[u],T,-t);
    		for(int v:g[u]) if(q[v])
    			add(q[u],q[v],inf);
    	}
    	for(int i=ql;i<=qr;i++) q[p[i]]=0;
    	while(bfs())
    	{
    		for(int i=S;i<=T;i++) cur[i]=f[i];
    		dfs(S,inf);
    	}
    	for(int i=ql;i<=qr;i++)
    		if(!d[i-ql+1]) L[++tl]=p[i];
    		else R[++tr]=p[i];
    	for(int i=1;i<=tl;i++) p[ql+i-1]=L[i];
    	for(int i=1;i<=tr;i++) p[ql+tl+i-1]=R[i];
    	cdq(ql,ql+tl-1,l,mid);
    	cdq(ql+tl,qr,mid+1,r);
    }
    signed main()
    {
    	n=read();m=read();
    	for(int i=1;i<=n;i++) c[i]=read();
    	for(int i=1;i<=n;i++) v[i]=read();
    	for(int i=1;i<=m;i++) a[i]=read();
    	for(int i=1;i<=m;i++) b[i]=read();
    	for(int i=1;i<=m;i++)
    	{
    		for(int j=0;j<64;j++) xs[j]=0;
    		for(int j=1;j<=m;j++)
    			if(i!=j) ins(c[a[j]]);
    		for(int j=1;j<=n;j++) if(j!=a[i])
    		{
    			int t=ins(c[j]);
    			if(~t) g[a[i]].pb(j),xs[t]=0;
    		}
    	}
    	for(int i=1;i<=m;i++)
    	{
    		for(int j=0;j<64;j++) xs[j]=0;
    		for(int j=1;j<=m;j++)
    			if(i!=j) ins(c[b[j]]);
    		for(int j=1;j<=n;j++) if(j!=b[i])
    		{
    			int t=ins(c[j]);
    			if(~t) g[j].pb(b[i]),xs[t]=0;
    		}
    	}
    	for(int i=1;i<=n;i++) p[i]=i;
    	cdq(1,n,0,1e6);int ans=0;
    	for(int i=1;i<=n;i++) ans+=calc(i,dp[i]);
    	printf("%lld\n",ans);
    }
    
  • 相关阅读:
    sql练手
    简单工厂模式和抽象工厂模式的区别:面向接口编程
    UML中聚合和组合的关系(笔记)
    如何修改SQL Server 2005服务器名称 (装载)
    .NET 图片处理剪裁
    sql server 中的汉字转化为拼音
    sql server while, case,if..else ... try catch ..对象
    String 和 StringBuilder 的相同点和不同点
    sql 折分字符串并修改数据库表中数据
    SQL Server 与 Excel,Access 数据表的导入导出(注:参照博园.NET大观)
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15933535.html
Copyright © 2020-2023  润新知