zoukankan      html  css  js  c++  java
  • 2018-常州集训提高组-测试一


    感谢一堆大佬把题带给我做。。。。。这题还是有点质量的,wzc大佬风采不减当年啊。

    总结

    1. 我不带文件过了,结果带了文件,没有编译过,失去了100分。
    2. 我以为DisjointSet的getRoot操作第二次和直接引用的耗时一样,事实证明我zz了
    3. 第二题注意数组开两倍,因为末端点和首端点混在一起离散化的,我调试数据的时候崩溃了一次,查了半天:(
    4. 第三题有点难搞,但是因为这个题给的Hint比较的明显,我好边界调了半天。

    动物园

    题意

    定义高兴值为一条路径上所经过节点的最小值,求整个图所有点的高兴值之和。

    题目分析

    这道题是一道很经典的最大瓶颈生成树的问题,数据范围你是无法直接枚举两个端点的,于是考虑统计每条边使用的次数,然后进行对答案的更新。
    一开始写的是Prim,但是发现后面没有用Kruskal方便,就换成了Kruskal。
    用了一个(r[i])表示序号,防止顺序被直接改变。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define forn(i, n) for(int i = 1; i <= (int)(n); i++)
    #define ford(i, n) for(int i = (int)(n) - 1; i >= 0; i--)
    
    typedef long long qword;
    
    const int inf = 0x7fffffff;
    
    const int maxn = 1e5 + 10;
    const int maxm = maxn * 10;
    
    int a[maxn], fa[maxn];
    int val[maxm], r[maxm], v[maxm], t[maxm];
    int cnt[maxm];
    
    bool cmp(int x, int y) {return val[x] > val[y];}
    int getRoot(int x) {return x == fa[x] ? x : fa[x] = getRoot(fa[x]);}
    #define OK
    int main() {
    #ifdef OK
        freopen("zoo.in","r",stdin);
        freopen("zoo.out","w",stdout);
    #endif // OK
        int n, m;
        qword ans = 0;
        cin >> n >> m;
        forn(i, n) cin >> a[i], fa[i] = i;
        int x, y;
        forn(i, m) {
            cin >> x >> y;
            val[i] = min(a[x], a[y]);
            v[i] = x; t[i] = y;
            r[i] = i; cnt[i] = 1;
        }
        sort(r + 1, r + m + 1, cmp);
        forn(i, m) {
            int k = r[i];
            int vv = getRoot(v[k]), tt = getRoot(t[k]);
            if (getRoot(v[k]) != getRoot(t[k])) {
                ans += 1ll * val[k] * cnt[vv] * cnt[tt];
                cnt[tt] += cnt[vv];
                fa[vv] = fa[tt];
            }
        }
        cout << ans * 2 << endl;
        return 0;
    }
    

    线段计数

    题目分析

    这道题相对于经典的线段计数问题,只是多了一个实时的添加和删除,直接套一个树状数组或者线段树就可以解决。
    然后因为这个数据有点点大,于是你再套一个离散化的板子,就很简单了。

    然而我因为没有去重,wa了一发(实测不去重可以A3个点)

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn = 500010*2;
    
    int C[2][maxn];
    struct Opt {
    	int opt, x, y;
        void Clear() {
            opt = x = y = 0;
        }
    }a[maxn];
    int b[maxn], cnt;
    int c[maxn], t;
    
    void Add(int opt, int x, int v, int n) {for(;x<=n*2;x+=x&-x) C[opt][x]+=v;}
    int Query(int opt, int x) {int res=0;for(;x;x-=x&-x) res+=C[opt][x];return res;}
    int query(int x, int y) {return Query(1,y) - Query(0,x-1);}
    void add(int x, int y, int n) { Add(0, x, 1, n); Add(1, y, 1, n);}
    void del(int x, int y, int n) { Add(0, x, -1, n);Add(1, y, -1, n);}
    
    #define rep(i,s,t) for(int i=(int)(s);i<=(int)(t);++i)
    #define forn(i) rep(i,1,n)
    
    inline void work() {
        int cnt = 0;
        int t = 0;
        int n;
        cin >> n;
        forn(i) {
            cin >> a[i].opt >> a[i].x;
            if(a[i].opt == 0) {
                c[++t] = i;
                b[++cnt] = a[i].x; b[++cnt] = a[i].x + t;
                a[i].y = a[i].x + t;
            }
        }
        sort(b + 1, b + 1 + cnt);
        cnt = unique(b + 1, b + 1 + cnt) - b - 1;
        forn(i) {
            if(!a[i].opt) {
                int x = lower_bound(b + 1, b + 1 + cnt, a[i].x) - b;
                int y = lower_bound(b + 1, b + 1 + cnt, a[i].y) - b;
                cout << query(x, y) << endl;
                add(x,y,n);
            } else {
                int x = lower_bound(b + 1, b + 1 + cnt, a[c[a[i].x]].x) - b;
                int y = lower_bound(b + 1, b + 1 + cnt, a[c[a[i].x]].y) - b;
                del(x,y,n);
            }
        }
    }
    
    inline void Init() {
        memset(C[1], 0, sizeof(C[1]));
        memset(C[0], 0, sizeof(C[0]));
    }
    #define OK
    int main() {
    #ifdef OK
        freopen("segment.in","r",stdin);
        freopen("segment.out","w",stdout);
    #endif // OK
        int T;
        ios::sync_with_stdio(0);
        cin >> T;
        rep(i,1,T) {
            cout << "Case #" << i << ":" << endl;
            Init();
            work();
        }
    	return 0;
    }
    

    填数

    题目分析

    易知, 至少会有一行/列是空的。

    我们不妨假设完全空的是一个行。我们把这行以外的元素随意填,只需满足每行的所有元素的积为−1.然后用这行来“收尾”。
    显然,这一行有且恰有一种方案来填充,以满足每列的所有元素积为(−1)的条件。于是,我们只需计算除这行外,每行有多少方案,使得这行的元素积为(−1),然后把这些结果乘起来即可。于是问题被转化为了1维上的问题。
    显然,答案只与『还需要填的格子数目』和『已经填上的格子里的数的积』有关。不妨设需要填(s)个格子,我们暴力枚举(k),表示其中(k)个格子填了(−1)(当然必须满足所有数的乘积为(−1)),则填法数目显然是(inom{s}{k}),其和即为答案。时间复杂度(O(nm))

    代码太多特判,就不写上来了。

  • 相关阅读:
    Open Auth辅助库(使用ImitateLogin实现登录)
    dotNet使用HttpWebRequest模拟浏览器
    开源一个社交网站模拟登录的库
    Storm系列(三):创建Maven项目打包提交wordcount到Storm集群
    Storm系列(二):使用Csharp创建你的第一个Storm拓扑(wordcount)
    Storm系列(一):搭建dotNet开发Storm拓扑的环境
    单点登录原理与简单实现
    策略模式Strategy
    MySQL 主从复制
    Windows下搭建MySql Master-Master Replication
  • 原文地址:https://www.cnblogs.com/Alessandro/p/9587779.html
Copyright © 2011-2022 走看看