卧薪尝胆,厚积薄发。
POI2010 TEL-Teleportation
Date: Thu Nov 01 08:25:55 CST 2018 In Category: NoCategory

Description:

给一张图,要求你再尽可能的多连边,使得从 $1$ 到 $2$ 至少要经过 $5$ 条边。
$1\leqslant n\leqslant 40000,1\leqslant m\leqslant 10^6$

Solution:

发现最后的情况可以把所有点划分成 $5$ 个集合,分别是 $1$ 所在的集合 $,1,3,4,2,2$ 所在的集合,每个集合是一个完全图,相邻两个集合之间两两有边,考虑怎么构造出这五个集合,首先发现 $1$ 所在的集合一定只有 $1$ ,因为 $1$ 所在集合中的点只能和 $1$ 所在集合中的点和 $1$ 集合连边,这样肯定不如放在 $1$ 集合中,除了上述边还可以和 $3$ 集合中的点连边,然后这样和 $1$ 或 $2$ 有边的点一定被分别分到了 $1$ 和 $2$ ,这时就该考虑和 $1$ 或 $2$ 有边的点该放在哪里,发现如果放在 $1$ 或 $2$ 里,那就可以和 $1$ , $1$ 集合和 $3$ 集合连边,但是由于集合 $4$ 的大小一定大于等于 $1$ ,所以不如舍弃和 $1$ 的边转而和 $4$ 集合连边,这时还剩一些点,于是就在 $3$ 和 $4$ 中找一个能连最多点的把他们都放进去肯定最优。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int n,m;
#define MAXN 40010
#define MAXM 1000010
struct edge
{
int to,nxt;
}e[MAXM << 1];
int edgenum = 0;
int lin[MAXN] = {0};
void add(int a,int b)
{
e[++edgenum] = (edge){b,lin[a]};lin[a] = edgenum;
e[++edgenum] = (edge){a,lin[b]};lin[b] = edgenum;
return;
}
int type[MAXN];
int main()
{
scanf("%d%d",&n,&m);
int a,b;
for(int i = 1;i <= m;++i)
{
scanf("%d%d",&a,&b);
add(a,b);
}
int tot1 = 0,tot2 = 0;
for(int i = lin[1];i != 0;i = e[i].nxt)
{
type[e[i].to] = 1;
++tot1;
}
for(int i = lin[2];i != 0;i = e[i].nxt)
{
type[e[i].to] = 2;
++tot2;
}
int tot3 = 0,tot4 = 0;
for(int k = 3;k <= n;++k)
{
if(type[k] != 0)continue;
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(type[e[i].to] == 0)continue;
if(type[e[i].to] == 1)++tot3;
else ++tot4;
break;
}
}
int rem = n - 2 - tot1 - tot2 - tot3 - tot4;
if(tot3 > tot4)tot3 += rem;
else if(tot4 > tot3)tot4 += rem;
else if(tot1 > tot2)tot3 += rem;
else tot4 += rem;
long long ans = 0;
ans += tot1 * (tot1 - 1) / 2 + tot2 * (tot2 - 1) / 2 + tot3 * (tot3 - 1) / 2 + tot4 * (tot4 - 1) / 2;
ans += tot1 + tot2 + tot1 * tot3 + tot2 * tot4 + tot3 * tot4;
cout << ans - m << endl;
return 0;
}
In tag: 算法-贪心
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡