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. VS2017+QT5.12.10+QGIS3.16环境搭建及开发全流程

    题记:大力发展生产力,助力高效采集.(转载请注明出处https://www.cnblogs.com/1024bytes/p/15477374.html) 本篇随笔分为五个部分: 一.获取QGIS3.1 ...

  2. Python:Ubuntu上出现错误 Could not load dynamic library 'libnvinfer.so.6' / 'libnvinfer_plugin.so.6'

    运行一个py文件,出现如下的错误,原因是没有找到 libnvinfer.so.6 相关库的文件. 1 2021-01-04 18:41:17.324477: W tensorflow/stream_e ...

  3. Spring Cache 带你飞(一)

    Spring 3.1 版本引入基于 annotation 的 cache 技术,提供了一套抽象的缓存实现方案,通过注解方式使用缓存,基于配置的方式灵活使用不同缓存组件.代码具有相当的灵活性和扩展性,本 ...

  4. DeWeb进阶 :控件开发 --- 1 完成一个纯html的demo

    最近随着DeWeb(以下简称DW)的完善,和群友的应用的深入,已经有网友开始尝试做DeWeb支持控件的开发了! 这太令人兴奋了! 作为DeWeb的开发者,感觉DeWeb的优势之一就是简洁的第三方控件扩 ...

  5. 基于Lucene的全文检索实践

    由于项目的需要,使用到了全文检索技术,这里将前段时间所做的工作进行一个实践总结,方便以后查阅.在实际的工作中,需要灵活的使用lucene里面的查询技术,以达到满足业务要求与搜索性能提升的目的. 一.全 ...

  6. MySQL——DML数据增、删、改

    插入 方式一(经典) 语法: insert into 表名(列名,...) values(值,...); 若要一次插入多行 create table if NOT EXISTS user( name ...

  7. JDK 之 Arrays.asList - 源码分析

    Arrays工具类提供了一个方法asList, 使用该方法可以将一个变长参数或者数组转换成List . 其源代码如下: @SafeVarargs public static <T> Lis ...

  8. 大爽Python入门教程 2-4 练习

    大爽Python入门公开课教案 点击查看教程总目录 方位输出 第一章有一个思考题,方位变换: 小明同学站在平原上,面朝北方,向左转51次之后(每次只转90度), 小明面朝哪里?小明转过了多少圈? (3 ...

  9. 痞子衡嵌入式:深扒IAR启动函数流程及其__low_level_init设计对函数重定向的影响

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是IAR启动函数流程及其__low_level_init设计对函数重定向的影响. 上一篇文章 <IAR下RT-Thread工程自定义 ...

  10. Nginx支持WebSocket反向代理

    WebSocket是目前比较成熟的技术了,WebSocket协议为创建客户端和服务器端需要实时双向通讯的webapp提供了一个选择.其为HTML5的一部分,WebSocket相较于原来开发这类app的 ...