2016第一场多校赛

时间:2023-01-09 09:34:32

爆0。。。总结一下

1001

题目

Problem Description

An abandoned country has n(n≤100000) villages which are numbered from 1 to n. Since abandoned for a long time, the roads need to be re-built. There are m(m≤1000000) roads to be re-built, the length of each road is wi(wi≤1000000). Guaranteed that any two wi are different. The roads made all the villages connected directly or indirectly before destroyed. Every road will cost the same value of its length to rebuild. The king wants to use the minimum cost to make all the villages connected with each other directly or indirectly. After the roads are re-built, the king asks a men as messenger. The king will select any two different points as starting point or the destination with the same probability. Now the king asks you to tell him the minimum cost and the minimum expectations length the messenger will walk.

Input

The first line contains an integer T(T≤10) which indicates the number of test cases.

For each test case, the first line contains two integers n,m indicate the number of villages and the number of roads to be re-built. Next m lines, each line have three number i,j,wi, the length of a road connecting the village i and the village j is wi.

Output

output the minimum cost and minimum Expectations with two decimal places. They separated by a space.

Sample Input

1
4 6
1 2 1
2 3 2
3 4 3
4 1 4
1 3 5
2 4 6

Sample Output

6 3.33

思路

首先注意到任意两条边的边权是不一样的,由此得知最小生成树是唯一的,最小生成树既然 是唯一的,那么期望其实也就是唯一的,不存在什么最小期望。求完最小生成树之后,接下 来的问题就可以转换成在最小生成树上求任意两点之间距离的平均值,对于每条边,统计所 有的路径用到此边的次数,也就是边的两端的点数之积。那么这条边的总贡献就是次数边 权。最后得到所有边的贡献之和再除以总路径数n∗(n−1)/2n(n-1)/2n∗(n−1)/2就是答案。可以OnOnOn求出。任取一点为根dfs,对每个点iii记录其子树包含的点数(包括其自身),设点数为sum[i]sum[i]sum[i],则iii的父亲一侧的点数即为n−sum[i]n-sum[i]n−sum[i]。一边遍历一边统计就行。

虽然思路很明了,但是实现起来确不是那么容易的。。。。主要难点是怎么把最小生成树保存下来。一开始想的是前向星,然而并不会写,乱写一通怎么都不对,后来看了标程发现可以用vector来保存与当前点相连的所有点的标号,这样每次只要遍历当前点的那个vector就能遍历所有与它直接相连的点了,然后dfs遍历到底。。。确实还是代码写的太少。。。。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#define ll long long
using namespace std;

const int maxn = 100000;

struct Edge {
    int x,y;
    int cost;
}g[1000000 + 10];

struct Node {
    int to;
    int val;
    Node(int _to, int _val)
        :to(_to), val(_val) {}
};

vector<Node>vet[maxn + 10];

int f[maxn + 10];
int num[maxn + 10];
ll res;
int n,m;

int MakeSet(int n) {
    for(int i = 1; i <= n; i++)
        f[i] = i;
}

int Findf(int x) {
    if(x != f[x]) {
        f[x] = Findf(f[x]);
    }
    return f[x];
}

bool cmp(Edge a, Edge b) {
    return a.cost < b.cost;
}

void Dfs(int root, int father) {
    num[root] = 1;
    for(int i = 0; i < vet[root].size(); i++) {
        int son = vet[root][i].to;
        int val = vet[root][i].val;
        if(son == father) continue;
        Dfs(son, root);
        num[root] += num[son];
        res += (ll)(num[son]) * (ll)(n - num[son]) * val;
    }   
}

int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d %d", &n, &m);
        for(int i = 1; i <= m; i++) {
            scanf("%d %d %d", &g[i].x, &g[i].y, &g[i].cost);
        }
        sort(g + 1, g + 1 + m, cmp);
        MakeSet(n);
        ll sum = 0;
        for(int i = 1; i <= n; i++) vet[i].clear();
        for(int i = 1; i <= m; i++) {
            int u = g[i].x;
            int v = g[i].y;
            int fu = Findf(u);
            int fv = Findf(v);
            if(fu != fv) {
                sum += g[i].cost;
                f[fu] = fv;
                vet[u].push_back(Node(v, g[i].cost));
                vet[v].push_back(Node(u, g[i].cost));
            }
        }
        memset(num, 0, sizeof(num));
        res = 0;
        Dfs(1,0);
        double expectations = (double)(res) / (double)(n);
        expectations = expectations / (double)(n - 1) * 2.0;
        printf("%lld %.2lf\n", sum, expectations);  
    }
    return 0;
}

1004

题目

Problem Description

Give you a sequence of N(N≤100,000) integers : a1,…,an(0

Input

The first line of input contains a number T, which stands for the number of test cases you need to solve.

The first line of each case contains a number N, denoting the number of integers.

The second line contains N integers, a1,…,an(0

Output

For each case, you need to output “Case #:t” at the beginning.(with quotes, t means the number of the test case, begin from 1).

For each query, you need to output the two numbers in a line. The first number stands for gcd(al,al+1,…,ar) and the second number stands for the number of pairs(l′,r′) such that gcd(al′,al′+1,…,ar′) equal gcd(al,al+1,…,ar).

Sample Input

1
5
1 2 4 6 7
4
1 5
2 4
3 4
4 4

Sample Output

Case #1:
1 8
2 4
2 4
6 1

思路

官方题解

我们注意观察gcd(al,al+1,…,ar)gcd(a_{l},a_{l+1},…,a_{r})gcd(a​l​​,a​l+1​​,…,a​r​​),当l固定不动的时候,r=l…nr=l…nr=l…n时,我们可以容易的发现,随着rrr的大,gcd(al,al+1,…,ar)gcd(a_{l},a_{l+1},…,a_{r})gcd(a​l​​,a​l+1​​,…,a​r​​)是递减的,同时gcd(al,al+1,…,ar)gcd(a_{l},a_{l+1},…,a_{r})gcd(a​l​​,a​l+1​​,…,a​r​​)最多 有log 1000,000,000log\ 1000,000,000log 1000,000,000个不同的值,为什么呢?因为ala_{l}a​l​​最多也就有log 1000,000,000log\ 1000,000,000log 1000,000,000个质因数

所以我们可以在log级别的时间处理出所有的以L开头的左区间的gcd(al,al+1,…,ar)gcd(a_{l},a_{l+1},…,a_{r})gcd(a​l​​,a​l+1​​,…,a​r​​) 那么我们就可以在n log 1000,000,000n\ log\ 1000,000,000n log 1000,000,000的时间内预处理出所有的gcd(al,al+1,…,ar)gcd(a_{l},a_{l+1},…,a_{r})gcd(a​l​​,a​l+1​​,…,a​r​​)然后我们可以用一个map来记录,gcd值为key的有多少个 然后我们就可以对于每个询问只需要查询对应gcd(al,al+1,…,ar)gcd(a_{l},a_{l+1},…,a_{r})gcd(a​l​​,a​l+1​​,…,a​r​​)为多少,然后再在map 里面查找对应答案即可.

自己想法

实际上前一段大家都明白,然而就是没想到用map来保存,map大家都知道,但是能活用看来还是不容易。这里用了三个map,ans记录的是结果,即ans[i]代表gcd为i的值有多少组,mp表示的是以a[i]结尾的所有区间的gcd的个数,temp相当于是中间变量。至于求区间gcd直接用线段树瞎搞一下。
队里gscsdlz是这样描述这道题的:这里暴力打表有一个概念就是,当我在计算2 4 6 7这组数据的时候,4 6和2 4 6的GCD都是2,那么当7和4 6, 2 4 6求GCD和的时候,实际上就是和2就求GCD和,结果为1,如果这里7之前GCD和为2的有n组,那么我们可以断定GCD和为1的组数肯定会增加n组,代码里面虽然使用了双重for循环,但是其实,第二个for循环用来遍历map里面记录的GCD和,而且我们可以知道数据量一旦大了,GCD和很多时候都是1了,所有这个map并不大。

代码

#include <iostream>
#include <cstdio>
#include <map>
#define ll long long
using namespace std;

const int maxn = 100000;

map<int, ll>ans;
map<int, ll>mp;
map<int, ll>temp;

int a[maxn + 10];
int segtree[maxn << 2];
int m_t;

int Gcd(int a, int b) {
    if(a == -1)
        return b;
    if(b == -1)
        return a;
    if(a % b == 0)
        return b;
    return Gcd(b, a % b);
}

void Build(int root, int l, int r) {
    if(l == r) {
        segtree[root] = a[++m_t];
        return;
    }
    int mid = (l + r) >> 1;
    Build(root << 1, l, mid);
    Build(root << 1|1, mid + 1, r);
    segtree[root] = Gcd(segtree[root << 1], segtree[root << 1|1]);
}

int Query(int root, int l, int r, int L, int R) {
    if(L <= l && R >= r) {
        return segtree[root];
    }
    int mid = (l + r) >> 1;
    int _gcd = -1;
    if(L <= mid)
        _gcd = Gcd(_gcd, Query(root << 1, l, mid, L, R));
    if(R > mid)
        _gcd = Gcd(_gcd, Query(root << 1|1, mid + 1, r, L ,R));
    return _gcd;
}

int main() {
    int T;
    //printf("%d\n", Gcd(6, 10));
    scanf("%d", &T);
    int ncase;
    for(ncase = 1; ncase <= T; ncase++) {
        printf("Case #%d:\n", ncase);
        mp.clear();
        temp.clear();
        ans.clear();
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) 
            scanf("%d", &a[i]);
        m_t = 0;
        Build(1,1,n);
        mp[a[1]]++;
        ans[a[1]]++;
        map<int, ll>::iterator it;
        for(int i = 2; i <= n; i++) {
            int now = a[i];
            temp[now]++;
            ans[now]++;
            for(it = mp.begin(); it != mp.end(); it++) {
                int next = Gcd(now, it->first);
                temp[next] += it->second;
                ans[next] += it->second;
            }
            mp.clear();
            mp = temp;
            temp.clear();
        }
        int m;
        scanf("%d", &m);
        while(m--) {
            int x,y;
            scanf("%d %d", &x, &y);
            int q = Query(1,1,n,x,y);
            printf("%d %lld\n", q, ans[q]);
        }
    }
    return 0;
}