打比赛时脑子落家了,没A出来,补题过的…
(另外题目在比赛时有修改,这里只说修改后的)
题意:给你一个图(N,E),n个点,m条双向边,每个点有一个值b[i],
你可以操作任意以下操作:
选择一个点,并将当前情况下从该点到达的所有点(包括该点),的值b都减1.
注意:当某个点的值b变成0时,这个点将不会进入后面的运算,相当于此时刻后该点与连此点的边都会消失.
问:你至少操作多少次才能将所有的值都减小为0.
思路:对b数组按照从大到小排序,for一遍将点v添加到图中,对每个点,先判断该点连接的互相不联通的图的个数c,则ans+=(v点的值bv)*(1-c)
跑完后ans就是结果了,
实现:判断是否联通用并查集维护就好了,
注意:结果会暴int,用long long存储,中间数也可能暴int,乘之前先转化成long long.
先查再并,否则可能会多算一个.
证明:其实我不会…
当图中加入一个新的点v时,原本没有联通的两(多)个子图将会联通,此时图中的任意一个点的值b都比加入的点的值b[v]大(或者等于),
那么
对任意一个与v有边的子图来说
进行b[v]次下述操作
{
{将原本对子图中某个点的操作}
转移到v
}
不会对原本的图有任何影响(当操作b[v]此后v与原来的子图分割).
这样会转移bvc次,只需要将操作次数提升bv(c-1)次数(甚至会减少).
为什么这样就是最优呢?
其实我不会…
//#pragma comment(linker, "/STACK:102400000,102400000")
//#pragma optimize("-O3")
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#ifdef Wang_Zhifeng
void debug_out() { cout << '\n'; }
template<typename T, typename... R>
void debug_out(const T &f, const R &... r) {
cout << f << " ";
debug_out(r...);
}
//#define debug(x) printf("debug:%s=%d\n",#x,x);
#define debug(...) cout << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__);
#endif
#define ll long long
#define MAXN 100005
int n, m;
ll ans;
int add2;
struct node {
int val;
int pos;
} b[MAXN];
vector<vector<int>> edge(MAXN);
int need[MAXN];
bool vis[MAXN];
bool cmp(node a, node b) {
return a.val > b.val;
}
class and_collect {
public:
int parent[MAXN];
int rank[MAXN];
///初始化
void init() {
for(int i = 0; i < MAXN; ++i) {
parent[i] = i;
rank[i] = 0;
}
};
///在查询的过程中实现了路径压缩
int find(int a) {
if(parent[a]==a) {
return a;
} else {
return parent[a] = find(parent[a]);
}
}
///在合并的过程中实现按照rank合并
void bing(int a, int b) {
a = find(a);
b = find(b);
if(a==b)
///可以根据需要将 将函数改写成带有查功能的函数
return;
if(rank[a] < rank[b])
parent[a] = b;
else {
parent[b] = a;
if(rank[a]==rank[b])
++rank[a];
}
}
bool cha(int a, int b) {
return find(a)==find(b);
}
};
and_collect andCollect;
void init() {
andCollect.init();
ans = 0;
for(int i = 0; i <= n; ++i) {
edge[i].clear();
vis[i] = false;
need[i] = 0;
}
}
void input() {
int ta, tb;
scanf("%d%d", &n, &m);
init();
for(int i = 1; i <= n; ++i) {
scanf("%d", &b[i].val);
b[i].pos = i;
}
while(m--) {
scanf("%d%d", &ta, &tb);
if(b[ta].val==b[tb].val) {
edge[ta].push_back(tb);
edge[tb].push_back(ta);
} else if(b[ta].val < b[tb].val) {
edge[ta].push_back(tb);
} else {
edge[tb].push_back(ta);
}
}
sort(b+1, b+1+n, cmp);
}
void solve() {
int t;
int tmp;
int len;
scanf("%d", &t);
while(t--) {
input();
for(int i = 1; i <= n; ++i) {
map<int, bool> mp;
add2 = 0;
len = edge[b[i].pos].size();
// debug(i)
// debug(b[i].pos, b[i].val)
for(int j = 0; j < len; ++j) {
if(!vis[edge[b[i].pos][j]])continue;
tmp = andCollect.find(edge[b[i].pos][j]);
if(!mp[tmp]) {
add2++;
mp[tmp] = true;
}
}
for(int j = 0; j < len; ++j) {
andCollect.bing(b[i].pos, edge[b[i].pos][j]);
}
need[andCollect.find(b[i].pos)] = b[i].val;
ans += 1ll*b[i].val*(1-add2);
vis[b[i].pos] = true;
}
printf("%lld\n", ans);
}
}
int main() {
#ifdef Wang_Zhifeng
freopen("in.txt", "r", stdin);
setvbuf(stdout, NULL, _IOFBF, 1024);
clock_t startTime, endTime;
startTime = clock();
//std::ios::sync_with_stdio(false);
//cin.tie(0);
#endif
solve();
#ifdef Wang_Zhifeng
endTime = clock();
cout << "time: " << (float) (endTime-startTime)*1000/CLOCKS_PER_SEC << " ms" << endl;
#endif
return 0;
}