2016多校赛1 A 期望 B SG博弈,状压 D 倍增,二分

2016 Multi-University Training Contest 1

A - Abandoned country

题意:给定一个图,1、求最小生成树的边权和。2、求最小生成树上任意两个点的距离的期望。

tags:第一问好求。第二问,ans=(所有点对距离和)/C(n,2), 这里转一下思维,所有点对距离和 --> 每条边要用到几次, 然后dfs搜一遍即可。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define fi first
#define se second
typedef long long ll;
const int N = 100005, M=1000005;

int n, m, sz[N], fa[N], head[N], tot1, tot2;
ll ans1, ans2;
pair<ll, pair<int ,int > > e1[M];
struct Edge{ int to, next; ll w; } e[M];
void Init() {
    tot1=tot2=0;  ans1=ans2=0;
    rep(i,1,n) fa[i]=i;
    mes(head, -1);
}
void Addedge(int u, int v, ll w) {
    e[tot2]={v, head[u], w}; head[u]=tot2++;
    e[tot2]={u, head[v], w}; head[v]=tot2++;
}
void dfs(int u, int fa)
{
    sz[u]=1;
    for(int i=head[u]; i!=-1; i=e[i].next) {
        int to=e[i].to;
        if(to!=fa) {
            dfs(to, u);
            sz[u]+=sz[to];
            ans2+= e[i].w*(n-sz[to])*sz[to];//*(e[i].w);    //一开始这里爆int 挖了
        }
    }
}
int Find(int x) { return fa[x]==x ? x : fa[x]=Find(fa[x]); }
bool Unite(int u, int v) {
    int fau=Find(u), fav=Find(v);
    if(fau==fav) return false;
    else { fa[fav]=fau; return true; }
}
void kruskal()
{
    sort(e1+1, e1+1+tot1);
    int num=0;
    rep(i,1,tot1) if(Unite(e1[i].se.fi, e1[i].se.se)) {
        num++,  ans1+=e1[i].fi;
        Addedge(e1[i].se.fi, e1[i].se.se, e1[i].fi);
        if(num==n-1) break;
    }
    printf("%lld ", ans1);
}
int main()
{
    int T;  scanf("%d", &T);
    while(T--)
    {
        scanf("%d %d", &n, &m);
        Init();
        int u, v;  ll w;
        rep(i,1,m) {
            scanf("%d %d %lld", &u, &v, &w);
            e1[++tot1]=MP(w, MP(u,v));
        }
        kruskal();
        dfs(1,0);
        printf("%.2f
", 1.0*ans2*2/n/(n-1));
    }

    return 0;
}
View Code

B - Chess

题意:一个n*20的棋盘,给出每行棋子的位置。如果棋子右边一格没有棋子,它可以移动到右边一格;如果右边一格有棋子,它可以跳过右边连着的所有棋子。 两个人轮流选一个棋子移动,不能移动者败,问先手胜负。

tags:20格,状压存下状态,推出sg即可。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
typedef long long ll;
const int N = 1<<21;

int n, m, ans, pi, sg[N];
int getsg(int x)
{
    bool vis[21];
    mes(vis, false);
    rep(i,1,20) if((x>>(i-1)&1)) {
        per(j,i-1,1) if(!((x>>(j-1))&1)) {
            int tmp=x^(1<<(i-1))^(1<<(j-1));
            vis[sg[tmp]]=1;
            break;
        }
    }
    rep(i,0,20) if(vis[i]==0) return i;
}
int main()
{
    rep(i,0,N-1) sg[i]=getsg(i);
    int T;  scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        ans=0;
        rep(ca,1,n) {
            scanf("%d", &m);
            int tmp=0;
            rep(i,1,m) {
                scanf("%d", &pi);
                tmp|= 1<<(20-pi);
            }
            ans^= sg[tmp];
        }
        if(ans==0) puts("NO");  else puts("YES");
    }

    return 0;
}
View Code

D - GCD

题意:给出数组a[],Q个询问,每次询问有 l , r 。求 1、区间[l, r]的gcd; 2、求有多少个区间的gcd和其相等。

tags: 好题

第一问好求,倍增预处理出即可。 重点是第二问,如何统计出gcd出现的次数。我们将左端点从1到n枚举,同时右端点从左端点开始往右边走。因为左端点固定,右端点往右走,gcd肯定是下降的,而且下降速度会很快,所以我们同时再二分处理即可,复杂度O(n*log(n)*log(n))。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
typedef long long ll;
const int N = 100005;

int n, Q, a[N], b[N][3], g[N][20];
map<int , ll > mp;
int query(int l, int r)
{
    int flr=log2(r-l+1), len=1<<flr;
    return __gcd(g[l][flr], g[r-len+1][flr]);
}
void Init()
{
    int tlog=log2(n);
    rep(j,1,n) g[j][0]=a[j];
    rep(i,1,tlog) {
        int len=1<<(i-1), nr=n-(1<<i)+1;
        rep(j,1,nr) g[j][i]=__gcd(g[j][i-1], g[j+len][i-1]);
    }
}
int main()
{
    int T;  scanf("%d", &T);
    rep(cas,1,T)
    {
        mp.clear();
        printf("Case #%d:
", cas);
        scanf("%d", &n);
        rep(i,1,n) scanf("%d", &a[i]);
        Init();
        scanf("%d", &Q);
        rep(i,1,Q) {
            scanf("%d %d", &b[i][0], &b[i][1]);
            b[i][2]=query(b[i][0], b[i][1]);
            mp.insert(make_pair(b[i][2], 0));
        }
        rep(i,1,n)
        {
            int ti=i;
            while(ti<=n) {
                int tmp=query(i, ti);
                int l=ti, r=n, ans1;
                while(l<=r) {
                    int mid=(l+r)>>1;
                    if(query(i,mid)>=tmp) l=mid+1, ans1=mid;
                    else  r=mid-1;
                }
                if(mp.find(tmp)!=mp.end()) mp[tmp]+= ans1-ti+1;
                ti=ans1+1;
            }
        }
        rep(i,1,Q) {
            printf("%d %lld
", b[i][2], mp[b[i][2]]);
        }
    }

    return 0;
}
View Code

K - tetrahedron

tags:计算几何,求四面体内接球的球心坐标和半径。  传送门

原文地址:https://www.cnblogs.com/sbfhy/p/6705547.html