2017-10-1 清北刷题冲刺班p.m

一道图论好题

(graph)

Time Limit:1000ms   Memory Limit:128MB

 

题目描述

LYK有一张无向图G={V,E},这张无向图有n个点m条边组成。并且这是一张带权图,不仅有边权还有点权。

LYK给出了一个子图的定义,一张图G’={V’,E’}被称作G的子图,当且仅当

·G’的点集V’包含于G的点集V。

·对于E中的任意两个点a,b∈V’,当(a,b)∈E时,(a,b)一定也属于E’,并且连接这两个点的边的边权是一样的。

LYK给一个子图定义了它的价值,它的价值为:点权之和与边权之和的比。

LYK想找到一个价值最大的非空子图,所以它来找你帮忙啦。

 

输入格式(graph.in)

    第一行两个数n,m表示一张n个点m条边的图。

    第二行n个数ai表示点权。

    接下来m行每行三个数u,v,z,表示有一条连接u,v的边权为z的无向边。数据保证任意两个点之间最多一条边相连,并且不存在自环。

 

输出格式(graph.out)

你需要输出这个价值最大的非空子图的价值,由于它是一个浮点数,你只需要保留小数点后两位有效数字。

 

输入样例

3 3

2 3 4

1 2 3

1 3 4

2 3 5

 

输出样例

1.67

 

样例解释

选择1,2两个点,则价值为5/3=1.67。

 

对于20%的数据n=2

对于50%的数据n<=5

对于100%的数据1<=n,m<=100000,1<=ai,z<=1000。

/*
    最优比率环??根本不会
    经过证明,最终答案是只选择一条边,求一个最大值 
*/
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <complex>
#include <string>
#include <cstring>
#include <vector>
#include <map>
using namespace std;
double ans;
int A,B,C,n,m,a[100005],i;
int main()
{
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (i=1; i<=n; i++) scanf("%d",&a[i]);
    for (i=1; i<=m; i++)
    {
        scanf("%d%d%d",&A,&B,&C);
        ans=max(ans,(a[A]+a[B])/(C+0.0));
    }
    printf("%.2f\n",ans);
    return 0;
}
AC代码

 

拍照

(photo)

Time Limit:1000ms   Memory Limit:128MB

 

题目描述

    假设这是一个二次元。

LYK召集了n个小伙伴一起来拍照。他们分别有自己的身高Hi和宽度Wi。

为了放下这个照片并且每个小伙伴都完整的露出来,必须需要一个宽度为ΣWi,长度为max{Hi}的相框。(因为不能叠罗汉)。

LYK为了节省相框的空间,它有了绝妙的idea,让部分人躺着!一个人躺着相当于是身高变成了Wi,宽度变成了Hi。但是很多人躺着不好看,于是LYK规定最多只有n/2个人躺着。(也就是说当n=3时最多只有1个人躺着,当n=4时最多只有2个人躺着)

LYK现在想问你,当其中部分人躺着后,相框的面积最少是多少。

 

输入格式(photo.in)

    第一行一个数n。

    接下来n行,每行两个数分别是Wi,Hi。

 

输出格式(photo.out)

你需要输出这个相框的面积最少是多少。

 

输入样例

3

3 1

2 2

4 3

 

输出样例

21

 

样例解释

如果没人躺过来,需要27的面积。

我们只要让第1个人躺过来,就只需要21的面积!

 

对于30%的数据n<=10。

对于60%的数据n<=1000,Wi,Hi<=10。

对于100%的数据1<=n,Wi,Hi<=1000。

/*
    贪心
    枚举最终状态最高身高为x
    分三种情况
    1.身高>x,宽度<=x,一定躺 
    2.身高<=x,宽度>x,一定不躺
    3.身高,宽度<=x
        1)身高>宽度 不能躺 
        2)身高<=宽度 只能让部分人躺,(宽度-身高)越大,越要躺 
*/
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <set>
#include <map>
using namespace std;
set<int> ::iterator sit;
int ans,sum,p[1005],i,a[1005],b[1005],cnt,CNT,j,ANS,n;
int cmp(int i,int j) {return i>j;}
bool FLAG;
int main()
{
    freopen("photo.in","r",stdin);
    freopen("photo.out","w",stdout);
    ANS=1000000000;
    scanf("%d",&n);
    for (i=1; i<=n; i++)
      scanf("%d%d",&a[i],&b[i]);
    for (i=1; i<=1000; i++)
    {
        sum=0; FLAG=true; cnt=0; CNT=0;
        for (j=1; j<=n; j++)
          if (b[j]<=i && (a[j]<=b[j] || a[j]>i)) sum+=a[j]; else
            if (a[j]>i && b[j]>i) {FLAG=false; break;} else
              if (b[j]>i) {cnt++; sum+=b[j];} else
              {
                  p[++CNT]=a[j]-b[j];
                  sum+=a[j];
              }
        if (!FLAG) continue;
        if (cnt>n/2) continue;
        sort(p+1,p+CNT+1,cmp);
        for (j=1; j<=min(n/2-cnt,CNT); j++) sum-=p[j];
        ANS=min(ANS,sum*i);
    }
    cout<<ANS;
    return 0;
}
AC代码 贪心

 

或和异或

(xor)

Time Limit:2000ms   Memory Limit:128MB

 

题目描述

LYK最近在研究位运算,它研究的主要有两个:or和xor。(C语言中对于|和^)

为了更好的了解这两个运算符,LYK找来了一个2^n长度的数组。它第一次先对所有相邻两个数执行or操作,得到一个2^(n-1)长度的数组。也就是说,如果一开始时a[1],a[2],…,a[2^n],执行完第一次操作后,会得到a[1] or a[2],a[3] or a[4] ,…, a[(2^n)-1] or a[2^n]。

第二次操作,LYK会将所有相邻两个数执行xor操作,得到一个2^(n-2)长度的数组,假如第一次操作后的数组是b[1],b[2],…,b[2^(n-1)],那么执行完这次操作后会变成b[1] xor b[2], b[3] xor b[4] ,…, b[(2^(n-1))-1] xor b[2^(n-1)]。

第三次操作,LYK仍然将执行or操作,第四次LYK执行xor操作。如此交替进行。

最终这2^n个数一定会变成1个数。LYK想知道最终这个数是多少。

为了让这个游戏更好玩,LYK还会执行Q次修改操作。每次修改原先的2^n长度的数组中的某一个数,对于每次修改操作,你需要输出n次操作后(最后一定只剩下唯一一个数)剩下的那个数是多少。

 

输入格式(xor.in)

    第一行两个数n,Q。

接下来一行2^n个数ai表示一开始的数组。

接下来Q行,每行两个数xi,yi,表示LYK这次的修改操作是将a{xi}改成yi。

 

输出格式(xor.out)

Q行,表示每次修改操作后执行n次操作后剩下的那个数的值。

 

输入样例

2 4

1 6 3 5

1 4

3 4

1 2

1 2

 

输出样例

1

3

3

3

 

样例解释

第一次修改,{4,6,3,5}->{6,7}->{1}

第二次修改,{4,6,4,5}->{6,5}->{3}

第三次修改,{2,6,4,5}->{6,5}->{3}

第四次修改,{2,6,4,5}->{6,5}->{3}

对于30%的数据n<=17,Q=1。

对于另外20%的数据n<=10,Q<=1000。

对于再另外30%的数据n<=12,Q<=100000。

对于100%的数据1<=n<=17,1<=Q<=10^5,1<=xi<=2^n,0<=yi<2^30,0<=ai<2^30。

/*
    倍增表
    st[j][i]表示i次操作后,j位置的数字是什么 
*/
#include <cmath>
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
long long st[131073][18];
int next[18][131073],i,j,n,m,now,k,A,B;
int main()
{
    freopen("xor.in","r",stdin);
    freopen("xor.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (i=1; i<=(1<<n); i++) cin>>st[i][0];
    for (i=1; i<=n; i++)
    {
        for (j=1; j<=(1<<n); j+=(1<<i))
        {
            if (i % 2==1) st[j][i]=st[j][i-1]|st[j+(1<<(i-1))][i-1]; else
              st[j][i]=st[j][i-1]^st[j+(1<<(i-1))][i-1];
        }
    }
    for (i=1; i<=n; i++)
      for (j=1; j<=(1<<n); j+=(1<<i))
      {
          for (k=j; k<=j+(1<<i)-1; k++)
            next[i][k]=j;
      }
    for (i=1; i<=m; i++)
    {
        scanf("%d%d",&A,&B);
        st[A][0]=B;
        for (j=1; j<=n; j++)
        {
            now=next[j][A];
            if (j % 2==1) st[now][j]=st[now][j-1]|st[now+(1<<(j-1))][j-1]; else
                st[now][j]=(st[now][j-1]^st[now+(1<<(j-1))][j-1]);
        }
        cout<<st[1][n]<<endl;
    }
    return 0;
}
AC代码 倍增表

 

posted @ 2017-10-01 16:05  Echo宝贝儿  阅读(349)  评论(0编辑  收藏  举报