2016多校1

HDU 5723 A  t 组样例 n个点m条边

然后m条无向边 

a  b  w

求他的最小生成树的  花费 

然后求  每2个点的花费的期望

先跑一个最小生成树  

然后呢   要求期望 先要求出一个 总体花费 然后 /(n*(n-1)/2)

那么怎么求花费呢     比如说样例     1 - 2 - 3 - 4

                                                  1   2  3

                用dfs 维护子树的点的数目

              那么这条边的贡献 就是num[孩子] *(n-num[孩子]) *w(边)

                           上面             下面

最后结果     

#include<stdio.h>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<stack>

using namespace std;

#define ll   __int64
#define MAXN  100010
#define inf  1000000000
#define mod 1000000007
struct edg
{
    int fr,to,w,next;
}edge[1000010],edg1[2000010];
int z[MAXN];
bool cmp(edg a,edg b)
{
    return a.w<b.w;
}
int find1(int a)
{
    if(z[a]==a)
        return a;
    else
    {
        int b=find1(z[a]);
        return z[a]=b;
    }
}
int cnt;
int head[MAXN];
void add(int u,int v,int w)
{
    edg1[cnt].to=v;
    edg1[cnt].w=w;
    edg1[cnt].next=head[u];
    head[u]=cnt++;
}
int sz[MAXN];
double ans1;
int n,m;
void dfs(int u,int fa)
{
    sz[u]=1;
    for(int i=head[u];i!=-1;i=edg1[i].next)
    {

        int v=edg1[i].to;

        if(v==fa)
            continue;
       // printf("%d\n",v);
        dfs(v,u);
        sz[u]=sz[u]+sz[v];
        ans1=ans1+ (double)sz[v]*(n-sz[v])*edg1[i].w;
        //printf("%d %lf\n",v,ans1);
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {

        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            z[i]=i;
        for(int i=1;i<=m;i++)
            scanf("%d%d%d",&edge[i].fr,&edge[i].to,&edge[i].w);
        sort(edge+1,edge+m+1,cmp);
        ll ans=0;
        cnt=0;
        memset(head,-1,sizeof(head));
        int c1=0;
        for(int i=1;i<=m;i++)
        {
            int fa,fb;
            fa=find1(edge[i].fr);
            fb=find1(edge[i].to);
            if(fa!=fb)
            {
                c1++;
                 z[fa]=fb;
                 ans+=edge[i].w;
                 add(edge[i].fr,edge[i].to,edge[i].w);
                 add(edge[i].to,edge[i].fr,edge[i].w);
            }
            if(c1==n-1)
                break;
        }
        printf("%I64d ",ans);
        ans1=0;
        dfs(1,0);
        printf("%.2lf\n",ans1/n/(n-1)*2);
    }
    return 0;
}
View Code

HDU 5724 B

t 个样例

n 行 

然后n行

m  m个数字

代表开始的位置

一次只能像右 走1个能跳过去  不能重叠  最多走到20

Alice 先走  他能营的话  YES否则N0

显然多行的话就是个尼姆博弈

那么一行呢 

看代码 

#include<stdio.h>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<iterator>
#include<stack>

using namespace std;

#define ll   __int64
#define MAXN  100010
#define inf  1000000000
#define mod 1000000007
bool vis[25];
int  sg[1<<21];

int get(int a)
{
    memset(vis,0,sizeof(vis));
    for(int i=20;i>=0;i--)  
    {
        if(a&(1<<i))  // 第i    是1
        {
            int t=a;
            for(int j=i-1;j>=0;j--) 
            {
                if(!(a&(1<<j))) //走到0的
                {
                    t^=(1<<i)^(1<<j); //把i 这个1去掉  j 这个1加上
                    vis[sg[t]]=1;     //这个位子能走到
                    break;
                }
            }

        }
    }
    for(int i=0;i<=20;i++)  //最多能走到20种  所以..
        if(!vis[i])
            return i;
}
int main()
{
    int t;
    scanf("%d",&t);
    for(int i=0;i<(1<<21);i++) //预处理出每个状态的sg
        sg[i]=get(i);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            int m;
            scanf("%d",&m);
            int now=0;
            for(int j=1;j<=m;j++)
            {
                int a;
                scanf("%d",&a);
                now|=1<<(20-a);  //这边每个就上去1
            }
            ans ^=sg[now];
        }
        if(ans==0)
            printf("NO\n");
        else
            printf("YES\n");
    }
    return 0;
}

D hdu 5726

t 组样例  n 个数

M 个查询

问所有区间里能和这个区间gcd一样的数目  长度为1的也算

输出gcd和数目

显然先要线段树维护一下gcd

然后暴力统计一下到z[i]的gcd的数目 ans[a]=b   代表gcd为a的区间数目为b

m1 是到z[i] 的gcd数目   m2临时的  ans  结果

#include<stdio.h>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<iterator>
#include<stack>

using namespace std;

#define ll   __int64
#define MAXN  100010
#define inf  1000000000
#define mod 1000000007
struct node
{
    int l,r,w;
}tree[MAXN<<2];
int z[MAXN];
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
void push_up(int a)
{
    tree[a].w=gcd(tree[a<<1].w,tree[a<<1|1].w);
}
void Build(int l,int r,int a)
{
    tree[a].l=l;
    tree[a].r=r;
    if(l==r)
    {
        tree[a].w=z[l];
        return ;
    }
    int mid=(l+r)>>1;
    Build(l,mid,a<<1);
    Build(mid+1,r,a<<1|1);
    push_up(a);
}
int Ques(int l,int r,int l1,int r1,int a)
{
    if(l1<=l&&r<=r1)
    {
        return tree[a].w;
    }
    int ans=0;
    int mid=(l+r)>>1;
    if(l1<=mid)
        ans = gcd(ans,Ques(l,mid,l1,r1,a<<1));
    if(r1>mid)
        ans = gcd(ans,Ques(mid+1,r,l1,r1,a<<1|1));
    return ans;
}
map<int,ll>ans,m1,m2;

int main()
{
    int t,ca;
    scanf("%d",&t);
    ca=1;
    while(t--)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&z[i]);
        Build(1,n,1);
        printf("Case #%d:\n",ca++);
        ans.clear();
        m1.clear();
        m2.clear();
        m1[z[1]]++;
        ans[z[1]]++;
        for(int i=2;i<=n;i++)
        {
            int now=z[i];
            m2[now]++;
            ans[now]++;
            for(map<int,ll>::iterator it=m1.begin();it!=m1.end();it++)
            {
                int g=gcd(now,it->first);
                m2[g]+=it->second;
                ans[g]+=it->second;
            }
            m1.clear();
            for(map<int,ll>::iterator it=m2.begin();it!=m2.end();it++)
            {
                m1[it->first]=it->second;
            }
            m2.clear();
        }
        int m;
        scanf("%d",&m);
        while(m--)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            int tmp=Ques(1,n,l,r,1);
            printf("%d %I64d\n",tmp,ans[tmp]);
        }
    }
    return 0;
}
View Code

 

 

           

转载于:https://www.cnblogs.com/cherryMJY/p/6613163.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值