2016 Multi-University Training Contest 1

A.Abandoned country

构建最小生成树,然后每条边的权值乘以两边的点数之积。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 1e5+5;
int n,m;
struct Edge{
    int u,v,w;
};
Edge edge[10*maxn];
vector<Edge> g[maxn];
int in[maxn];
int pre[maxn];
int vis[maxn];
typedef long long ll;
bool cmp(Edge A,Edge B)
{
    return A.w<B.w;
}
ll sum = 0;
double sum2 = 0;
int Find(int x){return pre[x] == x ? x : pre[x] = Find(pre[x]);}

int Merge(int x,int y)
{
    int fx = Find(x);
    int fy = Find(y);
    if(fx != fy)
    {
        pre[fx] = fy;
        return 1;
    }
    return 0;
}
void Kruskal()
{
    for(int i = 1; i<=m; i++)
    {
        if(Merge(edge[i].u,edge[i].v))
        {
            sum += edge[i].w;
            g[edge[i].u].push_back((Edge){edge[i].u,edge[i].v,edge[i].w});
            g[edge[i].v].push_back((Edge){edge[i].v,edge[i].u,edge[i].w});
            in[edge[i].u]++;
            in[edge[i].v]++;
        }
    }
}
void inin()
{
    for(int i = 1; i<=n; i++)
    {
        pre[i] = i;
        in[i] = 0;
        g[i].clear();
        vis[i] = 0;
    }
}
int dfs(int x)
{
    vis[x] = 1;
    int num = 1;
    for(int i = 0; i<g[x].size(); i++)
    {
        Edge cur = g[x][i];
        if(vis[cur.v]) continue;
        if(in[cur.v]==1)
        {
            sum2 += (double)cur.w*(n-1);
            num += 1;
            continue;
        }
        int ver = dfs(cur.v);
        sum2 += (double)cur.w*ver*(n-ver);
        num += ver;
    }
    return num;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d %d",&n,&m);
        inin();
       for(int i = 1; i<=m; i++)
       {
           scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].w);
       }
       sort(edge+1,edge+m+1,cmp);
       sum = 0;
       Kruskal();
       sum2 = 0;
       int i = 0;
       dfs(1);
       printf("%I64d %.2lf\n",sum,sum2*2/n/(n-1));
    }
    return 0;
}
/*
421
7 6
1 2 1
2 3 2
3 4 3
3 5 4
3 6 5
3 7 6
*/
卷珠帘

 

D.GCD

RMQ-ST预处理出gcd,复杂度n*logn*gcd.不过查询复杂度为log*gcd.

第二问枚举起点,对每个起点,gcd分段递减,对每一段二分出段的尾部,然后这段的gcd值都相同,用map记录,每个起点最多logn段。复杂度n*logn*logn*gcd。查询logn

 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
const int maxn = 1e5+5;
int n;
int cnt[maxn];
int M[maxn][20];
map<int,long long> g;
int gcd(int a,int b)
{
    return b == 0 ? a : gcd(b,a%b);
}
void inin()
{
    for(int i = 1; i<=n; i++) M[i][0] = cnt[i];
    for(int j = 1; (1 << j) <= n; j++)
        for(int i = 1; (i + (1 << j)-1) <= n; i++)
        M[i][j] = gcd( M[i][j-1] , M[i + (1 << (j-1))][j-1]);
}
int query(int l,int r)
{
    int k = 0;
    while((1 << (k+1))<=(r-l+1)) k++; //r-2^k+1>=l+2^k
    return gcd(M[l][k],M[r-(1 << k)+1][k]);
}
int binary(int s,int head,int g)
{
    int L = head;
    int R = n;
    int result = head;
    while(L<=R)
    {
        int mid = (L+R)/2;
        if(query(s,mid)==g)
        {
            result = mid;
            L = mid+1;
        }
        else R = mid-1;
    }
    return result;
}
void pre()
{
    g.clear();
    for(int i = 1; i<=n; i++)
    {
        int head = i;
        while(head<=n)
        {
            int Gcd = query(i,head);
            int tail = binary(i,head,Gcd);
            g[Gcd] += tail-head+1;
            head = tail+1;
        }
    }
}
int main()
{
    int t,kase = 0;
    cin>>t;
    while(t--)
    {
        printf("Case #%d:\n",++kase);
        scanf("%d",&n);
        for(int i = 1; i<=n; i++)
        {
            scanf("%d",&cnt[i]);
        }
        inin();
        pre();
        int q,l,r;
        cin>>q;
        for(int i = 1; i<=q; i++)
        {
            scanf("%d %d",&l,&r);
            int gg = query(l,r);
            printf("%d %I64d\n",gg,g[gg]);
        }
    }
    return 0;
}
卷珠帘

 

posted @ 2016-07-21 16:23  卷珠帘  阅读(241)  评论(0编辑  收藏  举报