2020牛客多校第二场01,05题
05:
New Equipments
链接:http://acm.hdu.edu.cn/showproblem.php?pid=6767
此题害人呀,建图是个技术活,万万没想到,我铁骨铮铮把图建,到头还栽建图上。
注意到m非常大,所以如果我们把每个n和每个m直接进行相连的话,肯定是过大的。题意要求我们的是每个人一个机器,那么我们每个人都与n个费用最小的机器进行相连,那就可以保证是完备匹配了。
所以,我们需要对每个人i找到函数ai*j*j+bi*j+c在1-m上的前n个最小值。
因为是一元二次函数,所以最小值的横坐标就是-(b/(a*2));但是注意到这有可能是个小数,所以我们向下取整得x,然后x和x+1在比较一下y值,可得到最小值的x值。
需要注意,算出来的x值有可能不在1-m的范围内,所以还需要比较判断一下。
建图完成之后,直接跑spfa就行。
此题,一直以为自己spfa错了,结果发现还是建图的问题。找bug找了一天呀 哭了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include"set"
#include"map"
using namespace std;
typedef long long ll;
inline int read(){
int s = 0, w = 1; char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') { s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar(); }
return s * w;
}
const ll inf=~0ULL>>1;
const int N=55,M=N*N+N+7,E=500005;
int ver[E], edge[E], Next[E], head[E];
ll cost[E],d[M];
int incf[M], pre[M], v[M];
int n, k, tot, s, t, maxflow,m,q[E];
ll ans,l,r;
ll a[N],b[N],c[N];
set<int> G[N],ALL;
map<int,int>ID;
void init(){
for(int i = 0; i <= n; i ++){G[i].clear();}
ALL.clear();ID.clear();
}
void add(int x, int y, int z, ll c) {
// 正向边,初始容量z,单位费用c
ver[++tot] = y, edge[tot] = z, cost[tot] = c;
Next[tot] = head[x], head[x] = tot;
// 反向边,初始容量0,单位费用-c,与正向边“成对存储”
ver[++tot] = x, edge[tot] = 0, cost[tot] = -c;
Next[tot] = head[y], head[y] = tot;
}
bool spfa() {
queue<int> q;
for(int i = 0; i <= t; i ++) {
d[i] = inf; // INF
v[i] = 0;
}
q.push(s); d[s] = 0; v[s] = 1; // SPFA 求最长路
incf[s] = 1LL << 30; // 增广路上各边的最小剩余容量
while (q.size()) {
int x = q.front(); v[x] = 0; q.pop();
for (int i = head[x]; i; i = Next[i]) {
if (!edge[i]) continue; // 剩余容量为0,不在残量网络中,不遍历
int y = ver[i];
if (d[y]>d[x] + cost[i]) {
d[y] = d[x] + cost[i];
incf[y] = min(incf[x], edge[i]);
pre[y] = i; // 记录前驱,便于找到最长路的实际方案
if (!v[y]) v[y] = 1, q.push(y);
}
}
}
if (d[t] == inf) return false; // 汇点不可达,已求出最大流
return true;
}
// 更新最长增广路及其反向边的剩余容量
void update() {
int x = t;
while (x != s) {
int i = pre[x];
edge[i] -= incf[t];
edge[i ^ 1] += incf[t]; // 利用“成对存储”的xor 1技巧
x = ver[i ^ 1];
}
maxflow += incf[t];
ans += d[t];
}
inline ll cal(ll a,ll b,ll x){return a*x*x+b*x;}
inline set<int> extend(ll a,ll b){
ll tmp=-(b/(a*2));
tmp-=1;
tmp=max(tmp,1LL);
tmp=min(tmp,1LL*m);
while(tmp<m&&cal(a,b,tmp)>cal(a,b,tmp+1))tmp++;
ll l=tmp,r=tmp+1;
set<int>ret;
ret.clear();
for(int i=1;i<=n;i++){
if(l<1){
ret.insert(r++);
continue;
}
if(r>m){
ret.insert(l--);
continue;
}
if(cal(a,b,l)<cal(a,b,r))ret.insert(l--);else ret.insert(r++);
}
for(set<int>::iterator it=ret.begin();it!=ret.end();it++)ALL.insert(*it);
return ret;
}
int main() {
int T = read();
while(T --){
n = read(); m = read();
init();
tot = 1;
for(int i = 1; i <= n; i ++){
scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
G[i]=extend(a[i],b[i]);
}
int top1 = 0;
for(set<int> :: iterator it = ALL.begin(); it != ALL.end(); it ++){
int x = *it;
ID[x] = ++ top1;
}
t = n + top1 + 2;
s = n + top1 + 1;
for(int i = 0; i <= t;i ++){
head[i] = 0;incf[i] = 0;pre[i] = 0;
}
for(int i = 1; i <= n; i ++){
for(set<int> :: iterator it = G[i].begin(); it != G[i].end(); it ++){
int x = *it;
ll f = a[i] * x * x + b[i] * x + c[i];
add(i,ID[x] + n,1,f);
}
}
for(int i = 1; i <= n; i ++){
add(s,i,1,0);
}
for(int i = 1; i <= top1; i ++){
add(i + n,t,1,0);
}
maxflow = ans = 0;
int x;
for(int i = 1; i <= n; i ++) {
spfa();update();
printf("%lld%c",ans,i<n?' ':'\n');
}
}
}
01:
Total Eclipse
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6763
此题直观想法就是找最大连通块,全部-1,在找最大连通块。
但是这样不好实现代码,所以我们可以考虑倒着来;
我们按亮度值,从大到小进行排序,然后依次加入并查集。
加入每个点 x 时遍历与 x 相连的所有边 (x, y),如果 y 在 x 之前加入且 x 和 y 不连通则将 x
和 y 合并,并将 y 所在连通块的树根的父亲设为 x。
那么我们可以发现每个点变成0,需要做的贡献是a[i] - a[fa[i]];
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include"set"
#include"map"
using namespace std;
#define inf 1e9+7
typedef long long ll;
inline int read(){
int s = 0, w = 1; char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') { s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar(); }
return s * w;
}
const int N = 100010, M = 200100;
int n,m,b[N],a[N];
int f[N],fa[N],vis[N];
int head[N],ver[M << 1],Next[M << 1],tot;
void init(){
tot = 0;
for(int i = 0; i <= n; i ++){
f[i] = i; vis[i] = 0;head[i] = 0;
fa[i] = 0;
}
}
void add(int x,int y){
ver[++ tot] = y; Next[tot] = head[x]; head[x] = tot;
}
int Find(int x){
if(x == f[x]) return x;
return f[x] = Find(f[x]);
}
int cmp(int x,int y){
return b[x] > b[y];
}
int main() {
int T = read();
while(T --){
n = read(); m = read();
init();
for(int i = 1; i <= n; i ++) {b[i] = read();a[i] = i;}
for(int i = 1; i <= m; i ++){
int x = read(),y = read();
add(x,y); add(y,x);
}
sort(a + 1,a + n + 1,cmp);
for(int i = 1; i <= n; i ++){
int x = a[i];
vis[x] = 1;
for(int j = head[x]; j ; j = Next[j]){
int y = ver[j];
if(vis[y] == 0) continue;
y = Find(y);
if(y == x) continue;
f[y] = x; fa[y] = x;
}
}
ll ans = 0;
for(int i = 1; i <= n; i ++)
ans += b[i] - b[fa[i]];
printf("%lld\n",ans);
}
}
浙公网安备 33010602011771号