Codeforces 739D - Recover a functional graph(二分图匹配)
首先假设我们已经填好了所有问号处的值怎样判断是否存在一个合法的构造方案,显然对于一种方案能够构造出合法的基环内向森林当且仅当:
- \(\forall x,(0,x)\) 的个数为 \(x\) 的倍数,否则不能构成若干个长度为 \(x\) 的环。
- 设 \(mx_y=\max\{x|(x,y)\in S\}\),其中 \(S\) 为所有二元组构成的集合,那么必然有二元组 \((0,y),(1,y),(2,y),\cdots,(mx_y,y)\in S\),否则感性理解一下可知必然存在某个二元组 \((x,y)\) 没地方连。
具体构造如下:
- 对于所有形如 \((0,x)\) 的二元组,我们将它们每 \(x\) 个分一组,然后每组内单独成环。
- 对于所有形如 \((x,y)(x>1)\) 的二元组,我们随便找一个 \((x-1,y)\) 连过去。
合法性显然。因此我们如果能够构造出一个合法的替换问号的方案,那么这题就做完了。那么怎么构造出合法的替换问号的方案呢?不难发现,这个模型可以转化为,你有若干个二元组 \((x,y)\),每个二元组还需要若干个,你还有若干个带问号的二元组,每个带问号的二元组可以通过将问号替换为数对某个二元组的数量产生最多 \(1\) 的贡献,问分配方案。看到这样的设问方式咱可以轻松想到二分图匹配,具体来说你将所有带问号的二元组放在左边,从源点向它们连容量 \(1\) 的边,需要的二元组放在右边,向汇点连边,容量为需要的二元组的个数,对于可以替换的部分就在中间连一条容量为 \(1\) 的边,跑最大流即可。
接下来考虑怎样具体实现。由于我们的构造方案需要满足以上两个性质,因此考虑将它们分开来考虑。首先 \((0,x)\) 是 \(x\) 的倍数这件事是好办的,如果我们记 \(c_{i,j}\) 表示二元组 \((i,j)\) 的个数,那么 \((0,x)\) 的个数至少是 \(\lceil\dfrac{c_{0,x}}{x}\rceil·x\) 个,当然有一个细节就是如果存在第二维为 \(x\) 的二元组,但不存在 \((0,x)\),此时 \((0,x)\) 的个数也应至少有 \(x\) 个,而不是上式算出来的 \(0\) 个,我们就将它看作一个二元组放在右部,连一条它到汇点,容量 \(\lceil\dfrac{c_{0,x}}{x}\rceil·x-c_{0,x}\) 的边表示还需要这么多个二元组 \((0,x)\)。比较棘手的是第二个限制,由于形如 \((x,?)\) 这样的二元组的存在,我们并不知道它对哪个位置的 \(mx\) 产生了影响,而暴力枚举费时费力,因此我们的做法貌似假掉了。但是有一个非常 trivial 的性质,就是在所有 \((x,?)\) 的二元组中,其实我们只用关心 \(x\) 最大的二元组即可,假设这样的二元组中 \(x\) 最大的二元组最终被替换为了 \((x,y)\),那么其他这样的二元组都把问号替换为 \(y\) 一定是合法的,因此考虑枚举这 \(x\) 最大的二元组替换为了什么,这样我们即可知道每个 \(y\) 的 \(mx_y\),这下子就好办了呗,对于所有 \(x\in[1,mx_y]\),如果 \(c_{x,y}=0\),就将 \((x,y)\) 看作一个需要的二元组放在右部,并向汇点连容量 \(1\) 的边。然后对于所有带问号的二元组,向可以替换的二元组连边并跑最大流即可。
最后有一些二元组的问号是没有替换的,对于 \((?,?)\) 的二元组我们直接将它替换为 \((0,1)\) 即可,\((?,x)\) 直接把 \(?\) 换位 \(1\) 即可,\((x,?)\) 稍微要分下类,如果 \(x=0\) 那 \(?\) 设为 \(1\) 即可,\(x\ne 0\) 就找到一个 \(mx_y\ge x\) 的 \(y\) 然后将 \(?\) 替换为 \(y\) 即可。
算下复杂度,由于最多只有 \(n\) 个二元组能够产生贡献,因此如果需要的二元组个数 \(>n\) 就直接返回不可行即可。因此点数是线性的,边数是平方的,再加上外层枚举的 \(n\),总复杂度 \(n^3\),可以通过此题。
细节有点多,一不小心又码了 \(190\) 行。
const int MAXN=300;
const int MAXV=1000;
const int MAXE=2e5;
const int INF=0x3f3f3f3f;
int n,m,a[MAXN+5],b[MAXN+5];
int parse(string s){
if(s=="?") return -1;int x=0;
for(int i=0;i<s.size();i++) x=x*10+s[i]-'0';
return x;
}
int cnt[MAXN+5][MAXN+5],mx[MAXN+5],id[MAXN+5][MAXN+5],has[MAXN+5];
int S=999,T=1000,hd[MAXV+5],to[MAXE+5],nxt[MAXE+5],cap[MAXE+5],ec=1;
void adde(int u,int v,int f){
// printf("%d %d %d\n",u,v,f);
to[++ec]=v;cap[ec]=f;nxt[ec]=hd[u];hd[u]=ec;
to[++ec]=u;cap[ec]=0;nxt[ec]=hd[v];hd[v]=ec;
} int dep[MAXV+5],now[MAXV+5];
bool getdep(){
memset(dep,-1,sizeof(dep));dep[S]=0;
queue<int> q;q.push(S);now[S]=hd[S];
while(!q.empty()){
int x=q.front();q.pop();
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=cap[e];
if(z&&!~dep[y]){
dep[y]=dep[x]+1;
now[y]=hd[y];q.push(y);
}
}
} return ~dep[T];
}
int getflow(int x,int f){
if(x==T) return f;int ret=0;
for(int &e=now[x];e;e=nxt[e]){
int y=to[e],z=cap[e];
if(dep[y]==dep[x]+1&&z){
int w=getflow(y,min(f-ret,z));
cap[e]-=w;cap[e^1]+=w;ret+=w;
if(ret==f) return ret;
}
} return ret;
}
int dinic(){
int res=0;
while(getdep()) res+=getflow(S,INF);
return res;
}
bool check(){
memset(cnt,0,sizeof(cnt));memset(mx,0,sizeof(mx));
memset(has,0,sizeof(has));memset(id,0,sizeof(id));
memset(hd,0,sizeof(hd));ec=1;int sum=0;m=0;
for(int i=1;i<=n;i++) if(~a[i]&&~b[i]){
cnt[a[i]][b[i]]++;
chkmax(mx[b[i]],a[i]);
} for(int i=1;i<=n;i++) adde(S,i,1);
for(int i=1;i<=n;i++) if(~b[i]) has[b[i]]=1;
// for(int i=1;i<=n;i++) printf("%d%c",mx[i]," \n"[i==n]);
for(int i=1;i<=n;i++) if(mx[i]||cnt[0][i]||has[i]){
int need=(cnt[0][i]+i-1)/i*i;
if(!need) need=i;
if(need^cnt[0][i]){
if((id[0][i]=++m)>n) return 0;
// printf("id[0][%d]=%d\n",i,m);
adde(m+n,T,need-cnt[0][i]);
sum+=need-cnt[0][i];
// printf("%d %d\n",i,need-cnt[0][i]);
}
for(int j=1;j<=mx[i];j++) if(!cnt[j][i]){
if((id[j][i]=++m)>n) return 0;
// printf("id[%d][%d]=%d\n",j,i,m);
adde(m+n,T,1);sum++;
}
}
for(int i=1;i<=n;i++){
if(~a[i]&&~b[i]) continue;
else if(~a[i]){
// printf("%d\n",i);
for(int j=1;j<=n;j++) if(id[a[i]][j])
adde(i,id[a[i]][j]+n,1);
} else if(~b[i]){
// printf("%d\n",i);
for(int j=0;j<=n;j++) if(id[j][b[i]])
adde(i,id[j][b[i]]+n,1);
} else {
for(int j=1;j<=m;j++) adde(i,j+n,1);
// printf("%d\n",i);
}
} return (dinic()==sum);
}
void work(){
for(int i=0;i<=n;i++) for(int j=1;j<=n;j++){
if(id[i][j]){
int k=id[i][j];
// printf("%d %d %d\n",i,j,k);
for(int e=hd[k+n];e;e=nxt[e]){
// printf("%d\n",e);
if(to[e]!=T&&cap[e]){
int x=to[e];
// printf("x=%d %d %d\n",x,a[x],b[x]);
assert(!~a[x]||a[x]==i);
assert(!~b[x]||b[x]==j);
a[x]=i;b[x]=j;
// printf("%d %d\n",a[x],b[x]);
}
}
}
}
for(int i=1;i<=n;i++) if(!~a[i]||!~b[i]){
if(!~a[i]&&!~b[i]) a[i]=0,b[i]=1;
else if(!~a[i]) a[i]=1;
else{
if(!a[i]) b[i]=1;
else{
for(int j=1;j<=n;j++) if(mx[j]>=a[i])
b[i]=j;
}
}
}
// for(int i=1;i<=n;i++) printf("%d %d\n",a[i],b[i]);
}
vector<int> vec[MAXN+5];
int ans[MAXN+5];
void makegraph(){
for(int i=1;i<=n;i++) if(!a[i]) vec[b[i]].pb(i);
for(int i=1;i<=n;i++) if(!vec[i].empty()){
assert(vec[i].size()%i==0);
for(int j=0;j<vec[i].size();j+=i){
for(int k=j;k<j+i-1;k++) ans[vec[i][k]]=vec[i][k+1];
ans[vec[i][j+i-1]]=vec[i][j];
}
}
for(int i=1;i<=n;i++) if(a[i])
for(int j=1;j<=n;j++) if(a[j]==a[i]-1&&b[j]==b[i]){
ans[i]=j;
}
for(int i=1;i<=n;i++) printf("%d%c",ans[i]," \n"[i==n]);
}
int main(){
scanf("%d",&n);int mx=0;
for(int i=1;i<=n;i++){
string s,t;cin>>s>>t;
a[i]=parse(s);b[i]=parse(t);
if(~a[i]&&!~b[i])
if(a[i]>a[mx]) mx=i;
}
for(int i=1;i<=n;i++){
// printf("check %d\n",i);
if(mx) b[mx]=i;
if(check()) return work(),makegraph(),0;
if(mx) b[mx]=-1;
} puts("-1");
return 0;
}
Codeforces 739D - Recover a functional graph(二分图匹配)的更多相关文章
- Codeforces Round #548 (Div. 2) E 二分图匹配(新坑) or 网络流 + 反向处理
https://codeforces.com/contest/1139/problem/E 题意 有n个学生,m个社团,每个学生有一个\(p_i\)值,然后每个学生属于\(c_i\)社团, 有d天,每 ...
- Codeforces Educational Codeforces Round 15 E - Analysis of Pathes in Functional Graph
E. Analysis of Pathes in Functional Graph time limit per test 2 seconds memory limit per test 512 me ...
- [Codeforces 1027 F] Session in BSU [并查集维护二分图匹配问题]
题面 传送门 思路 真是一道神奇的题目呢 题目本身可以转化为二分图匹配问题,要求右半部分选择的点的最大编号最小的一组完美匹配 注意到这里左边半部分有一个性质:每个点恰好连出两条边到右半部分 那么我们可 ...
- HDOJ 5093 Battle ships 二分图匹配
二分图匹配: 分别按行和列把图展开.hungary二分图匹配. ... 例子: 4 4 *ooo o### **#* ooo* 按行展开. .. . *ooo o#oo oo#o ooo# **#o ...
- hdu 5943(素数间隔+二分图匹配)
Kingdom of Obsession Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Oth ...
- csu 1552(米勒拉宾素数测试+二分图匹配)
1552: Friends Time Limit: 3 Sec Memory Limit: 256 MBSubmit: 723 Solved: 198[Submit][Status][Web Bo ...
- [ZJOI2009]假期的宿舍 BZOJ 1433 二分图匹配
题目描述 学校放假了 · · · · · · 有些同学回家了,而有些同学则有以前的好朋友来探访,那么住宿就是一个问题.比如 A 和 B 都是学校的学生,A 要回家,而 C 来看B,C 与 A 不认识. ...
- [SCOI2010]连续攻击游戏 BZOJ1854 二分图匹配
题目描述 lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示.当他使用某种装备时,他只能使用该装备的某一个属性.并且每种装备 ...
- codeforces 1269D. Domino for Young (二分图证明/结论题)
链接:https://codeforces.com/contest/1269/problem/D 题意:给一个不规则的网格,在上面放置多米诺骨牌,多米诺骨牌长度要么是1x2,要么是2x1大小,问最多放 ...
随机推荐
- javascriptRemke之类的继承
前言:es6之前在js中要实现继承,就必须要我们程序员在原型链上手动继承多对象的操作,但是结果往往存在漏洞,为解决这些问题,社区中出现了盗用构造函数.组合继承.原型式继承.寄生式继承等一系列继承方式, ...
- ES2020新特性记录
1.可选链操作符 // oldlet ret = obj && obj.first && obj.first.second// newlet ret = obj?.fi ...
- JSP(java server pages)安装开发和执行环境
JSP是一种动态网页技术标准. 它是在传统的网页HTML文件中插入Java程序段(Scriptlet)和JSP标记(tag)的.jsp文件: java程序段:操纵数据库,重新定向网页,发送email等 ...
- 【Java虚拟机4】Java内存模型(硬件层面的并发优化基础知识--缓存一致性问题)
前言 今天学习了Java内存模型第一课的视频,讲了硬件层面的知识,还是和大学时一样,醍醐灌顶.老师讲得太好了. Java内存模型,感觉以前学得比较抽象.很繁杂,抽象. 这次试着系统一点跟着2个老师学习 ...
- .Net 5下的单文件部署
由于.net程序没有静态链接,一直缺乏单文件部署这种干净的发布方案.对客户端程序发布并不是很友好.在之前的.net framework下,有ILMerge合并程序集,以及LibZ的嵌入资源文件等第三方 ...
- 技术博客--微信小程序canvas实现图片编辑
技术博客--微信小程序canvas实现图片编辑 我们的这个小程序不仅仅是想给用户提供一个保存和查找的平台,还希望能给用户一个展示自己创意的舞台,因此我们实现了图片的编辑部分.我们对对图片的编辑集成了很 ...
- postman_参数关联
注:postman中的参数引用符号是{{变量名}},两个大括号 1.创建登录接口请求,在Tests中,添加如下代码,并send: // 获取响应的json数据 var jsonData = pm.re ...
- 【转】PLI是什么以及怎么用
programmable language interface 这里就说给verilog用的一些系统函数,还是无双大大的帖子 首先介绍了怎么让你自己写的pli系统函数在ncverilog里面可以成功调 ...
- sort-list leetcode C++
Sort a linked list in O(n log n) time using constant space complexity. C++ /** * Definition for sing ...
- 第k短路(Dijkstra & A*)
最短路,即第1短路有很多种求法,SPFA,Dijkstra等,但第k短路怎么求呢?其实也是基于Dijkstra:因为Dijkstra用的是堆优化,这样保证每次弹出来的都是最小值,只是求最短路只是弹出一 ...