zoukankan      html  css  js  c++  java
  • 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?' ':'
    ');
            }
        }
    
    }
    

      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
    ",ans);
        }
    
    }
    

      

  • 相关阅读:
    qt中qmake的详解
    教程:从零开始 使用Python进行深度学习!
    win10系统下搭建Python开发环境和TensorFlow深度学习环境(CPU版)
    怎么选择视觉光源颜色
    pycharm安装及设置中文
    新建DataSet和DataTable,并从中提取数据到文本
    网站服务基础面试
    TCP、UDP数据包大小的限制
    TCP的三次握手与四次挥手理解及面试题(很全面)
    zabbix服务深入
  • 原文地址:https://www.cnblogs.com/yrz001030/p/13374865.html
Copyright © 2011-2022 走看看