比较有思维含量的一道题

题意:给混合完全图定向(定向为竞赛图)使得有最多的三元环

三元环条件要求比较高,还不容易分开处理。

正难则反

考虑,什么情况下,三元组不是三元环

一定是一个点有2个入度,一个点有2个出度,另一个点一个入度,一个出度

也就是说,每存在一个>=2入度的点,那么会减少一些三元环

进而考虑,如果一个点有d个入度,那么减少的三元环其实是:C(d,2),即,包括它自己,再包括任意两个指向它的点(这里,a指向b,代表a能赢b),构成的三元组都不是三元环

考虑每个点作为某些个非法三元组的话,那么,

总共的三元环是:C(n,3)-∑C(du[i],2)

C(du[i],2)统计了所有与i有关的非法三元组,所以不重不漏统计完了。

怎样最小化这个∑?

定向,就是某些点的入度增加的过程。所以考虑某个点增加一个入度,减少的三元环的数量是多少。

即C(d+1,2)-C(d,2)=d即减少原来度数的三元环

这个减少是逐一增加的,n*(n-1)/2是下凸函数,可以考虑拆边费用流。

这个题的具体做法是:

把每个要定向的边看做一个点,从S到这个点连(1,0),意义是只能确定一个方向

这个点向所代表的边的两个原图端点连(1,0)的边,意义是增加入度,且只能给一个增加

每个原图 节点向T连(1,d),(1,d+1)...(1,d+n-2)的边,意义是,每增加一个入度,就会增加d的代价

最小费用最大流,spfa恰好先选择d,再选择d+1,,,,刚好符合实际的代价

最大流之后,每个边都定向完毕,而且增加的代价也都是对的。

至于输出方案,找每个边的代表点,看其哪一侧流量是0,就是哪一侧输。

代码:

#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
char ch;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=;
const int inf=0x3f3f3f3f;
int n,s,m,t;
struct node{
int nxt,to,w,v;
}e[*(N*N+N*N*+N*N)];
int hd[N+N*N],cnt=;
void add(int x,int y,int w,int v){
e[++cnt].nxt=hd[x];
e[cnt].to=y;
e[cnt].v=v;
e[cnt].w=w;
hd[x]=cnt; e[++cnt].nxt=hd[y];
e[cnt].to=x;
e[cnt].w=;
e[cnt].v=-v;
hd[y]=cnt;
}
int mp[N][N];
int op[N][N];
queue<int>q;
bool vis[N+N*N];
int dis[N*N+N];
int incf[N*N+N],pre[N*N+N];
bool spfa(){
while(!q.empty()) q.pop();
memset(dis,inf,sizeof dis);
dis[s]=;
q.push(s);
pre[s]=;
incf[s]=inf;
while(!q.empty()){
int x=q.front();q.pop();
vis[x]=;
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(e[i].w){
if(dis[y]>dis[x]+e[i].v){
dis[y]=dis[x]+e[i].v;
pre[y]=i;
incf[y]=min(incf[x],e[i].w);
if(!vis[y]){
vis[y]=;
q.push(y);
}
}
}
}
}
if(dis[t]==inf) return false;
return true;
}
int cos,maxflow;
int du[N];
void upda(){
int x=t;
while(pre[x]){
e[pre[x]].w-=incf[t];
e[pre[x]^].w+=incf[t];
x=e[pre[x]^].to;
}
cos+=incf[t]*dis[t];
maxflow+=incf[t];
}
int num(int i,int j){
return n+(i-)*(n-)+j;
}
int main(){
rd(n);
s=,t=n+n*n+;
for(reg i=;i<=n;++i){
for(reg j=;j<=n;++j){
rd(mp[i][j]);
if(mp[i][j]==&&i<j){
add(s,num(i,j),,);
add(num(i,j),i,,);
add(num(i,j),j,,);
}else if(mp[i][j]==){
du[j]++;
}
}
}
int ans=n*(n-)*(n-)/; for(reg i=;i<=n;++i){
ans-=du[i]*(du[i]-)/;
for(reg j=du[i];j<=n-;++j){
add(i,t,,j);
}
}
while(spfa()) upda();
ans-=cos;
printf("%d\n",ans);
memcpy(op,mp,sizeof mp);
for(reg i=;i<=n;++i){
for(reg j=;j<=n;++j){
if(mp[i][j]==&&i<j){
int x=num(i,j);
for(reg p=hd[x];p;p=e[p].nxt){
int y=e[p].to;
if(y!=s&&e[p].w==){
if(y==j){
op[i][j]=;
op[j][i]=;
}else{
op[i][j]=;
op[j][i]=;
}
}
}
}
}
}
for(reg i=;i<=n;++i){
for(reg j=;j<=n;++j){
printf("%d ",op[i][j]);
}puts("");
}
return ;
} }
signed main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2018/12/15 11:01:16
*/

总结:

值得学习的是:

1.正难则反,考虑非法的三元组,这样可以通过度数直接分开计算

2.边点转化,对无向图定向、而且贡献和点的入度有关,可以尝试采取这种策略。

3.下凸函数拆边费用流。因为下凸函数,所以最小费用的时候,每次会先选择最小的,然后往右或者往左选,那么拆边,实际上真正选择的恰好也符合实际情况。

[WC2007]剪刀石头布——费用流的更多相关文章

  1. BZOJ.2597.[WC2007]剪刀石头布(费用流zkw)

    BZOJ 洛谷 \(Description\) 给定一张部分边方向已确定的竞赛图.你需要给剩下的边确定方向,使得图中的三元环数量最多. \(n\leq100\). \(Solution\) 这种选择之 ...

  2. BZOJ 2597: [Wc2007]剪刀石头布(费用流)

    传送门 解题思路 考虑全集-不能构成三元环的个数.如果三个点不能构成三元环,一定有一个点的入度为\(2\),继续扩展,如果一个点的度数为\(3\),则会失去3个三元环.对于一个点来说,它所产生的不能构 ...

  3. bzoj 2597 [Wc2007]剪刀石头布——费用流

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2597 三个人之间的关系,除了“剪刀石头布”,就是有一个人赢了2局:所以考虑算补集,则每个人对 ...

  4. Luogu4249 WC2007 石头剪刀布 费用流

    传送门 考虑竞赛图三元环计数,设第\(i\)个点的入度为\(d_i\),根据容斥,答案为\(C_n^3 - \sum C_{d_i}^2\) 所以我们需要最小化\(\sum C_{d_i}^2\) 考 ...

  5. [bzoj2597][Wc2007]剪刀石头布_费用流

    [Wc2007]剪刀石头布 题目大意:https://www.lydsy.com/JudgeOnline/problem.php?id=2597 题解: 发现直接求三元环不好求,我们考虑任选三个点不是 ...

  6. 【bzoj2597】[Wc2007]剪刀石头布 动态加边费用流

    题目描述 在一些一对一游戏的比赛(如下棋.乒乓球和羽毛球的单打)中,我们经常会遇到A胜过B,B胜过C而C又胜过A的有趣情况,不妨形象的称之为剪刀石头布情况.有的时候,无聊的人们会津津乐道于统计有多少这 ...

  7. BZOJ2597 [Wc2007]剪刀石头布 【费用流】

    题目链接 BZOJ2597 题解 orz思维差 既然是一张竞赛图,我们选出任意三个点都可能成环 总方案数为 \[{n \choose 3}\] 如果三个点不成环,会发现它们的度数是确定的,入度分别为\ ...

  8. bzoj2597: [Wc2007]剪刀石头布(费用流)

    传送门 不得不说这思路真是太妙了 考虑能构成三元组很难,那我们考虑不能构成三元组的情况是怎么样 就是说一个三元组$(a,b,c)$,其中$a$赢两场,$b$赢一场,$c$没有赢 所以如果第$i$个人赢 ...

  9. bzoj 2597: [Wc2007]剪刀石头布【最小费用最大流】

    脑子不太清楚一个zz问题调了好久-- 首先正难则反,因为三元环好像没什么特点,就考虑让非三元环个数最小 考虑非三元环特点,就是环上一定有一个点的入度为2,联系整张图,三元环个数就是每个点C(入度,2) ...

随机推荐

  1. Hive初识(一)

    LOAD DATA语句 一般来说,在SQL创建表后,我们就可以使用INSERT语句插入数据.但在Hive中,可以使用LOAD DATA语句来插入数据. LOAD DATA [LOCAL] INPATH ...

  2. (转)Updates were rejected because the tip of your current branch is behind

    刚创建的github版本库,在push代码时出错: $ git push -u origin masterTo git@github.com:******/Demo.git ! [rejected] ...

  3. Ruby字符串的一些方法

    最近因为公司需求开始看ruby,先从ruby的基本数据类型开始看 看到ruby的字符串类型string,发现ruby中的字符串单双引号是不一样的,这点和Python有那么点不一样 主要是我们对字符串进 ...

  4. 基于STM32F103的Max30100心率、血氧检测代码(转载)

    MAX30100是能够读取心率.血氧的传感器,通信方式是通过IIC进行通信.其工作原理是通过红外led灯照射,能够得到心率的ADC值.       MAX30100的寄存器可以分为五类,状态寄存器.F ...

  5. 牛客暑假多校第六场I-Team Rocket

    一.题意 我们是穿越银河的火箭队....... 给出若干个区间,之后给出若干个点,要求对每个点求出,第一个覆盖点的区间的数量,之后用当前所有点覆盖的区间的序号的乘积结合输入的Y来生成下一位点.最后输出 ...

  6. SAPFiori

    最新SAP Fiori常用事务代码持续更新中...谢谢支持   注意: 以 / 开头的事务码需要加/N或/O进入,否则进不去   SEGW:  创建Gateway Service   /UI2/FLP ...

  7. 一步一步学Linq to sql(四):查询句法

    select 描述:查询顾客的公司名.地址信息 查询句法: var 构建匿名类型1 = from c in ctx.Customers select new { 公司名 = c.CompanyName ...

  8. struts2官方 中文教程 系列十一:使用XML进行表单验证

    在本教程中,我们将讨论如何使用Struts 2的XML验证方法来验证表单字段中用户的输入.在前面的教程中,我们讨论了在Action类中使用validate方法验证用户的输入.使用单独的XML验证文件让 ...

  9. c#根据ip获取城市地址

    用的API是百度.新浪.淘宝: 1.首先是一个检测获取的值是不是中文的方法,因为有的ip只能识别出来某省,而城市名称则为空返回的json里会出现null或undefined. public stati ...

  10. iOS-Debug Symbol(调试符号)

    Debug Symbol(调试符号) 编译警告 从svn下载下来的文件,到处都是编译警告,看着不爽,找下原因,没想到还是一条大鱼 warning: (i386) /UsersLibrary/Devel ...