2022/2/10 模拟赛
啥也不会……还没有题解给我……做什么啊。
反正这篇水……不如公开了。
同校的可以找我要密码。当然也可以自己猜。
一般图带权多重匹配
考场:100/100(0/100,拜 tr)
注意到不会做,那就万能网络流。
考虑到不好处理 \((i,i)\) 的贡献。注意到奇数个数不多,先假设只有偶数。我们将 \((i,j)\) 和 \((j,i)\) 看成不同的匹配方法(\((i,i)\) 同理拆成两个),将 \(a_i\) 均分。\(S\) 向 \(1 \sim i\) 连 \(\dfrac{a_{1 \sim i}}{2}\),\(1 \sim i\) 连向 \(n+1 \sim 2n\),\(n+1 \sim 2n\) 连向 \(T\)。费用流量都显然,最小费用最大流即可。
但是有个奇数……注意到多出来的这个我们不知道是谁的入度或出度,全部枚举一下就好了。'
/*
他决定要“格”院子里的竹子。于是他搬了一条凳子坐在院子里,面对着竹子硬想了七天,结果因为头痛而宣告失败。
DONT NEVER AROUND . //
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read()
{
int x=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
return x;
}
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
int cnt;
int siz[1005],nxt[100005],val[100005],cst[100005],to[100005];
inline void addEdge(int u,int v,int w,int c){++cnt,to[cnt]=v,nxt[cnt]=siz[u],val[cnt]=w,cst[cnt]=c,siz[u]=cnt;}
int n,a[55],c[55][55],K,od[15],p[15],s,t;
void build()
{
memset(siz,0,sizeof siz);
cnt=1;
s=2*n+1,t=s+1;
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) addEdge(i,j+n,10000,c[i][j]),addEdge(j+n,i,0,-c[i][j]);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=K;++j)
{
if(i==od[j])
{
if(p[j])
{
addEdge(s,i,a[i]/2+1,0);
addEdge(i,s,0,0);
addEdge(i+n,t,a[i]/2,0);
addEdge(t,i+n,0,0);
}
else
{
addEdge(s,i,a[i]/2,0);
addEdge(i,s,0,0);
addEdge(i+n,t,a[i]/2+1,0);
addEdge(t,i+n,0,0);
}
goto spec;
}
}
addEdge(s,i,a[i]/2,0);
addEdge(i,s,0,0);
addEdge(i+n,t,a[i]/2,0);
addEdge(t,i+n,0,0);
spec:;
}
}
int ntw[1005],dis[1005];
int pre[1005],lid[1005];
bool vis[1005];
bool bfs()
{
memset(ntw,63,sizeof ntw);
memset(dis,63,sizeof dis);
pre[t]=-1;
queue<int> Q;
Q.push(s);
dis[s]=0;
while(!Q.empty())
{
int now=Q.front();
Q.pop();
vis[now]=false;
for(int i=siz[now];i;i=nxt[i])
{
int v=to[i];
if(val[i] && dis[now]+cst[i]<dis[v])
{
dis[v]=dis[now]+cst[i];
lid[v]=i;
pre[v]=now;
ntw[v]=min(ntw[now],val[i]);
if(!vis[v])
{
vis[v]=true;
Q.push(v);
}
}
}
}
return ~pre[t];
}
int solve()
{
int cost=0;
while(bfs())
{
cost+=dis[t]*ntw[t];
int now=t;
while(now!=s)
{
val[lid[now]]-=ntw[t];
val[lid[now]^1]+=ntw[t];
now=pre[now];
}
}
return cost;
}
int main(){
n=read();
for(int i=1;i<=n;++i) K+=(a[i]=read())&1;
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) c[i][j]=read();
if(K&1) return puts("-1")&0;
K=0;
for(int i=1;i<=n;++i) if(a[i]&1) od[++K]=i;
for(int i=K/2+1;i<=K;++i) p[i]=1;
int ans=2e9;
do
{
build();
ans=min(ans,solve());
}while(next_permutation(p+1,p+1+K));
write(ans);
return 0;
}
排序
考场:0/100(43/100,拜 tr)
只会 \(O(2^k n\sum m)\)。大概思路是定义 \(dp_{i,l}\) 为结点 \(i\) 中构成的序列值域在 \([l,dp_{i,l}]\) 之内(显然 \(dp_{i,l}\) 越小越好),我们只关心加进去了哪些不关心顺序可以 \(O(2^k n\sum m)\) 转移。傻逼出题人还要求输出方案,我就觉得离谱。
写不出来。
感染
不会就是不会,怎么做也不会。