CF1264E. Beautiful League

【题目链接】https://codeforces.com/contest/1264/problem/E

【题解】

考虑三元环的数量如何计算。

由于是完全有向图(即竞赛图),分别考虑每三个点会不会构成三元环。

不难发现,三个点若不构成三元环一定存在且仅存在一个点出度为 $2$(入度同理)。

因此考虑计算不构成三元环的三个点数量,即 $\sum_{i=1}^{n} C_{deg_i}^{2}$(其中 $deg_i$ 为出度/入度)。

因此总三元环个数为 $C_{n}^{3}-\sum_{i=1}^{n} C_{deg_i}^{2}$。

考虑给每条边定向会对三元环个数造成什么影响。

显然定向会使其中一个点的度数增加。

根据公式 $C_{k+1}^{2}-C_{k}^{2}=k$,定向会使答案减少点的度数个三元环。

我们考虑本题的限制:每条边都一定要定向,不同方向会造成不同贡献,要求贡献最小(即三元环个数最大)。

这是一个最小费用最大流的模型。考虑如何建图。

首先 $S$ 向每条边代表的点连边,流量为 $1$,费用为 $0$,表示必须选择。

其次每条边代表的点向边的两端点连边,流量为 $1$,表示两个方向需要选择一个方向。

考虑费用如何计算。注意到当度数增加时,边的贡献也会改变。但任意时刻连向同一个点的边贡献相同。

考虑经典做法——拆点。将每个点拆成两个,分别连流量为 $1$,费用为 $deg_i,deg_i+1,deg_i+2,...$ 的边即可。

同时拆完后的后面的点往 $T$ 连流量为 $n$(其实并不会到 $n$,最多到 $n-deg_i-1$,上方拆点间连边同理),费用为 $0$ 的边。

同时上述边向点连边的费用为 $0$。建图跑最小费用最大流即可。

输出方案同一般费用流,判断满流情况。

 1 #include<bits/stdc++.h>
 2 const int inf=1<<30;
 3 const int maxn=100000;
 4 const int maxm=500000;
 5 struct Flow
 6 {
 7     int n,h[maxn],cnt=1,S,T,dis[maxn],fr[maxn];
 8     bool vis[maxn];
 9     struct edge { int v,nxt,f,w; } e[maxm];
10     inline void addedge ( int u,int v,int f,int w )
11     {
12         e[++cnt].nxt=h[u];e[h[u]=cnt].v=v;e[cnt].f=f;e[cnt].w=w;
13         e[++cnt].nxt=h[v];e[h[v]=cnt].v=u;e[cnt].f=0;e[cnt].w=-w;
14     }
15     inline bool spfa_min ( void )
16     {
17         for ( int i=1;i<=n;i++ ) vis[i]=false,fr[i]=0,dis[i]=inf;
18         std::queue<int> q;dis[S]=0;q.push(S);vis[S]=true;
19         while ( !q.empty() )
20         {
21             int u=q.front();q.pop();vis[u]=false;
22             for ( int i=h[u];i;i=e[i].nxt ) if ( e[i].f and dis[e[i].v]>dis[u]+e[i].w )
23             {
24                 dis[e[i].v]=dis[u]+e[i].w;fr[e[i].v]=i;
25                 if ( !vis[e[i].v] ) q.push(e[i].v),vis[e[i].v]=true;
26             }
27         }
28         return dis[T]!=inf;
29     }
30     inline int mincost ( void )
31     {
32         int ans=0;
33         while ( spfa_min() )
34         {
35             int flow=inf;
36             for ( int i=T;i!=S;i=e[fr[i]^1].v ) flow=std::min(flow,e[fr[i]].f);
37             ans+=dis[T]*flow;
38             for ( int i=T;i!=S;i=e[fr[i]^1].v ) e[fr[i]].f-=flow,e[fr[i]^1].f+=flow;
39         }
40         return ans;
41     }
42 } Gmin;
43 int n,m,N,G[200][200],ans[200][200],d[200];
44 signed main()
45 {
46     scanf("%d%d",&n,&m);
47     for ( int i=1,u,v;i<=m;i++ ) scanf("%d%d",&u,&v),d[u]++,ans[u][v]=1,G[u][v]=G[v][u]=-1;
48     N=n<<1;Gmin.S=++N;Gmin.T=++N;
49     for ( int i=1;i<n;i++ ) for ( int j=i+1;j<=n;j++ ) if ( ~G[i][j] )
50     {
51         G[i][j]=G[j][i]=++N;
52         Gmin.addedge(Gmin.S,G[i][j],1,0);
53         Gmin.addedge(G[i][j],i,1,0);
54         Gmin.addedge(G[i][j],j,1,0);
55     }
56     int Ans=n*(n-1)*(n-2)/6;
57     for ( int i=1;i<=n;i++ )
58     {
59         Ans-=d[i]*(d[i]-1)/2;
60         for ( int j=1;j<=n;j++ ) Gmin.addedge(i,i+n,1,d[i]+j-1);
61     }
62     for ( int i=1;i<=n;i++ ) Gmin.addedge(i+n,Gmin.T,n,0);
63     Gmin.n=N;Gmin.mincost();
64     for ( int i=1;i<n;i++ ) for ( int j=i+1;j<=n;j++ ) if ( ~G[i][j] )
65         for ( int k=Gmin.h[G[i][j]];k;k=Gmin.e[k].nxt ) if ( Gmin.e[k].v!=Gmin.S and !Gmin.e[k].f ) ans[Gmin.e[k].v][i+j-Gmin.e[k].v]=true;
66     for ( int i=1;i<=n;i++,puts("") ) for ( int j=1;j<=n;j++ ) putchar(ans[i][j]?'1':'0');
67     return 0;
68 }
CF1264E

官方题解&$std$ 在此做法的基础上使用了更复杂的算法,类似模拟费用流的贪心。由于作者水平有限,并不会这种做法。具体请参见:https://codeforces.com/blog/entry/71995

posted @ 2020-01-31 16:12  DTOI_RSY  阅读(215)  评论(0编辑  收藏  举报