国庆清北刷题冲刺班 Day1 下午

95 篇文章 1 订阅
75 篇文章 0 订阅

一道图论好题(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。
一个鲜明的结论:max(a/b,c/d)>= (a+c)/(b+d) 【这句是我自己推出来的,腿老师是不会告诉你答案的】

//只要你的脑子不是装饰品
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 100005
inline void read(int &x){
    x=0; int f=1; char c=getchar();
    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }
    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f;
}
int a[MAXN],n,m;
struct Edge{ int u,v,w; double k; }e[MAXN*2];

inline bool cmp(Edge x,Edge y){
    return x.k > y.k;
}

int main(int argc,char *argv[]){
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    read(n),read(m);
    for(int i=1;i<=n;++i) read(a[i]);
    for(int i=1;i<=m;++i){
        read(e[i].u),read(e[i].v),read(e[i].w);
        e[i].k=(a[e[i].u]+a[e[i].v])/(double)e[i].w;
    }
    sort(e+1,e+m+1,cmp);
    printf("%.2lf\n",e[1].k);
    fclose(stdin);fclose(stdout);
    return 0;
}

拍照(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。

/*
正解说是DP也好,贪心也罢  思想一样 
正解 : 枚举最大高度  检验最小面积
对于当前枚举的最大高度
对于 i
    wi  > i  hi < i 一定不能躺过来
    wi  < i  hi > i 一定要躺过来
    wi > i   hi > i ==>  需要 换个 高一点的 最大高度 
    wi < i  hi < i {
        1. 身高达与宽度 不能躺
        2. 身高小于等于宽度 只让部分人躺过来 ( 宽度-身高比较大的那一部分 ) 
    }
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 1005
const int INF = 0x7fffffff;
int h[MAXN],w[MAXN],tot,cnt,e[MAXN],Ans=INF;

inline void read(int &x){
    x=0; int f=1; char c=getchar();
    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }
    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f;
}

inline bool cmp(int a,int b){ return a > b; }

int main(int argc,char *argv[]){
    freopen("photo.in","r",stdin);
    freopen("photo.out","w",stdout);
    int n;
    read(n);
    for(int i=1; i<=n; ++i) read(w[i]),read(h[i]);
    for(int Max=1; Max<=1000; ++Max){
        bool flag = false;
        int Sum=0;
        cnt = tot = 0;
        for(int i=1; i<=n; ++i){
            if(h[i] > Max && w[i] > Max) {
                flag=true; break;//  换高度吧 
            }
            if(h[i] > Max && w[i] <= Max) ++cnt,Sum += h[i]; //一定要 躺过来 
            else{
                if(h[i] <= Max && (w[i] > Max || h[i] >= w[i])) Sum += w[i];
                else if(h[i] <= Max && w[i] <= Max && h[i] < w[i]) e[++tot] = w[i] - h[i],Sum += w[i];
            }
        }
        if(flag) continue;
        if(cnt > (n>>1)) continue;
        sort(e+1,e+tot+1,cmp);
        for (int i = 1; i <= min(n/2 - cnt,tot); ++i) Sum -= e[i];
        Ans = min(Ans,Sum * Max);
    }
    printf("%d\n",Ans);
    fclose(stdin);fclose(stdout);
    return 0;
}

或和异或(xor)

Time Limit:1000ms 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。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 1000010
int n,q,Max,c[MAXN];

inline void read(int &x){
    x=0; int f=1; char c=getchar();
    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }
    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); }  x*=f;
}

void Build(int u,int l,int r,int dep){
    if(l==r){ read(c[u]); return ; }
    int Mid = l+r >> 1;
    Build(u<<1,l,Mid,dep+1);
    Build(u<<1|1,Mid+1,r,dep+1);
    if((n-dep+1) & 1 ) c[u] = c[u<<1] | c[u<<1|1];
    else c[u] = c[u<<1] ^ c[u<<1|1];
}

void UpDate(int u,int l,int r,int pos,int v,int dep){
    if(l==r){ c[u]=v; return; }
    int Mid = l+r >> 1;
    if(pos<=Mid) UpDate(u<<1,l,Mid,pos,v,dep + 1);
    else UpDate(u<<1|1,Mid+1,r,pos,v,dep+1);
    if((n - dep +1) & 1) c[u] = c[u<<1] | c[u<<1|1];
    else c[u] = c[u<<1] ^ c[u<<1|1];
}

int main(int argc,char *argv[]){
    freopen("xor.in","r",stdin);
    freopen("xor.out","w",stdout);
    read(n),read(q);
    int l,r;
    Max = (1 << n);
    Build(1,1,Max,1);
    while(q--){
        read(l),read(r);
        UpDate(1,1,Max,l,r,1);
        printf("%d\n",c[1]);
    }
    fclose(stdin);fclose(stdout);
    return 0;
}

记录一下深度的线段树操作,但是是真的没想出来,ZlycerQan大佬用的不知道什么操作,水过去了。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七情六欲·

学生党不容易~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值