Codeforces 题面传送门 & 洛谷题面传送门

首先假设我们已经填好了所有问号处的值怎样判断是否存在一个合法的构造方案,显然对于一种方案能够构造出合法的基环内向森林当且仅当:

  1. \(\forall x,(0,x)\) 的个数为 \(x\) 的倍数,否则不能构成若干个长度为 \(x\) 的环。
  2. 设 \(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(二分图匹配)的更多相关文章

  1. Codeforces Round #548 (Div. 2) E 二分图匹配(新坑) or 网络流 + 反向处理

    https://codeforces.com/contest/1139/problem/E 题意 有n个学生,m个社团,每个学生有一个\(p_i\)值,然后每个学生属于\(c_i\)社团, 有d天,每 ...

  2. 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 ...

  3. [Codeforces 1027 F] Session in BSU [并查集维护二分图匹配问题]

    题面 传送门 思路 真是一道神奇的题目呢 题目本身可以转化为二分图匹配问题,要求右半部分选择的点的最大编号最小的一组完美匹配 注意到这里左边半部分有一个性质:每个点恰好连出两条边到右半部分 那么我们可 ...

  4. HDOJ 5093 Battle ships 二分图匹配

    二分图匹配: 分别按行和列把图展开.hungary二分图匹配. ... 例子: 4 4 *ooo o### **#* ooo* 按行展开. .. . *ooo o#oo oo#o ooo# **#o ...

  5. hdu 5943(素数间隔+二分图匹配)

    Kingdom of Obsession Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Oth ...

  6. csu 1552(米勒拉宾素数测试+二分图匹配)

    1552: Friends Time Limit: 3 Sec  Memory Limit: 256 MBSubmit: 723  Solved: 198[Submit][Status][Web Bo ...

  7. [ZJOI2009]假期的宿舍 BZOJ 1433 二分图匹配

    题目描述 学校放假了 · · · · · · 有些同学回家了,而有些同学则有以前的好朋友来探访,那么住宿就是一个问题.比如 A 和 B 都是学校的学生,A 要回家,而 C 来看B,C 与 A 不认识. ...

  8. [SCOI2010]连续攻击游戏 BZOJ1854 二分图匹配

    题目描述 lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示.当他使用某种装备时,他只能使用该装备的某一个属性.并且每种装备 ...

  9. codeforces 1269D. Domino for Young (二分图证明/结论题)

    链接:https://codeforces.com/contest/1269/problem/D 题意:给一个不规则的网格,在上面放置多米诺骨牌,多米诺骨牌长度要么是1x2,要么是2x1大小,问最多放 ...

随机推荐

  1. PHP文件上传漏洞与一句话木马

    靶子代码: 前端效果: 这是个没有任何防护的文件上传代码,同时还热心的附上了上传文件的路径. 我们写好php木马后,什么额外工作也不需要做,直接上传就行了.上传后在浏览器里访问该文件,其就会被执行. ...

  2. 《手把手教你》系列技巧篇(三十四)-java+ selenium自动化测试-单选和多选按钮操作-中篇(详解教程)

    1.简介 今天这一篇宏哥主要是讲解一下,如何使用list容器来遍历单选按钮.大致两部分内容:一部分是宏哥在本地弄的一个小demo,另一部分,宏哥是利用JQueryUI网站里的单选按钮进行实战. 2.d ...

  3. px,dp sp是像素、尺寸、尺寸

    px:即像素,1px代表屏幕上一个物理的像素点:px单位不被建议使用,因为同样100px的图片,在不同手机上显示的实际大小可能不同,如下图所示(图片来自android developer guide, ...

  4. Java:抽象类和接口小记

    Java:抽象类和接口小记 对 Java 中的 抽象类和接口,做一个微不足道的小小小小记 抽象类:使用 abstract 修饰,子类用 extends 继承: 接口:使用 interface 修饰,采 ...

  5. 微信小程序的实现原理

    一.背景 网页开发,渲染线程和脚本是互斥的,这也是为什么长时间的脚本运行可能会导致页面失去响应的原因,本质就是我们常说的 JS 是单线程的 而在小程序中,选择了 Hybrid 的渲染方式,将视图层和逻 ...

  6. Qt坐标转换系统的理解

    转 https://blog.csdn.net/hgcprg/article/details/53537106 今天又看了一篇对Qt坐标转换系统以及QTransform的博客,作者讲的非常透彻,链接如 ...

  7. OTA测试介绍

    OTA 测试介绍 手机的无源测试和有源测试 当前在手机射频性能测试中越来越关注整机辐射性能的测试,这种辐射性能反映了手目前主要有两种方法对手机的辐射性能进行考察:一种是从天线是目前较为传统的天线测试方 ...

  8. TVS管性能及选型总结

    https://wenku.baidu.com/view/5b5bda5526fff705cc170af8.html

  9. 使用jQuery-UI来实现一个Ajax的自动完成功能(自动填充搜索框的下拉值)

    首先你要在.net拓展包中去搜索  jquery ui (Combined Libray)安装这么个文件 第二部   在控制器中添加我们根据输入搜索框的值获取符合的记录集的action 第三步  有了 ...

  10. 在Express 中获取表单请求体数据

    在Express 中获取表单请求体数据 获取 GET 请求参数 获取 POST 请求体数据 安装 配置 获取 GET 请求参数 Express 内置了一个 API , 可以直接通过 req.query ...