ybt扩展题库14027~14030题解

唯一一个在扩展题库上做对这几题中的每一题的人,此刻你正在看他的文章。

​

14027:[HEOI2015]兔子与樱花

时间限制: 1000 ms         内存限制: 262144 KB
提交数: 109    通过数: 1
【题目描述】
很久很久之前,森林里住着一群兔子。有一天,兔子们突然决定要去看樱花。兔子们所在森林里的樱花树很特殊。樱花树由n个树枝分叉点组成,编号从0到n-1,这n个分叉点由n-1个树枝连接,我们可以把它看成一个有根树结构,其中0号节点是根节点。这个树的每个节点上都会有一些樱花,其中第i个节点有c_i朵樱花。樱花树的每一个节点都有最大的载重m,对于每一个节点i,它的儿子节点的个数和i节点上樱花个数之和不能超过m,即son(i) + c_i <= m,其中son(i)表示i的儿子的个数,如果i为叶子节点,则son(i) = 0

现在兔子们觉得樱花树上节点太多,希望去掉一些节点。当一个节点被去掉之后,这个节点上的樱花和它的儿子节点都被连到删掉节点的父节点上。如果父节点也被删除,那么就会继续向上连接,直到第一个没有被删除的节点为止。

现在兔子们希望计算在不违背最大载重的情况下,最多能删除多少节点。

注意根节点不能被删除,被删除的节点不被计入载重。

【输入】
第一行输入两个正整数,n和m分别表示节点个数和最大载重

第二行n个整数c_i,表示第i个节点上的樱花个数

接下来n行,每行第一个数k_i表示这个节点的儿子个数,接下来k_i个整数表示这个节点儿子的编号

【输出】
 一行一个整数,表示最多能删除多少节点。

【输入样例】
10 4
0 2 2 2 4 1 0 4 1 1
3 6 2 3
1 9
1 8
1 1
0
0
2 7 4
0
1 5
0
【输出样例】
4
【提示】
对于100%的数据,1 <= n <= 2000000, 1 <= m <= 100000, 0 <= c_i <= 1000

数据保证初始时,每个节点樱花数与儿子节点个数之和大于0且不超过m

【来源】
没有写明来源

​
	
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 2000005
using namespace std;
char ch;
int n,k,b,cnt,sum[maxn],tot,now[maxn],son[maxn],pre[maxn],f[maxn],g[maxn],list[maxn];
bool ok;
void read(int &x){
    for (ok=0,ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') ok=1;
    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
    if (ok) x=-x;
}
void put(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}
void dfs(int u){
    int st=cnt;
    for (int p=now[u],v=son[p];p;p=pre[p],v=son[p]) dfs(v),f[u]+=f[v],list[++cnt]=g[v]-1;
    sort(list+st+1,list+cnt+1);
    for (int i=st+1;i<=cnt&&g[u]+list[i]<=k;i++) g[u]+=list[i],f[u]++;
    cnt=st;
}
int main(){
    read(n),read(k);
    for (int i=0;i<n;i++) read(g[i]);
    for (int i=0;i<n;i++){
        read(sum[i]);
        for (int j=1;j<=sum[i];j++) read(b),put(i,b);
    }
    for (int i=0;i<n;i++) g[i]+=sum[i];
    dfs(0);
    printf("%d\n",f[0]);
    return 0;
}

14028:[HEOI2015]公约数数列

时间限制: 1000 ms         内存限制: 262144 KB
提交数: 13    通过数: 1
【题目描述】
设计一个数据结构. 给定一个正整数数列 a0,a1,...,an−1
,你需要支持以下两种操作:

1. MODIFY id x: 将 aid
 修改为 x
.

2. QUERY x: 求最小的整数 p(0<=p<n)
,使得 gcd(a0,a1,...,ap)∗XOR(a0,a1,...,ap)=x
. 其中 XOR(a0,a1,...,ap)
 代表 a0,a1,...,ap
 的异或和,gcd
表示最大公约数。

【输入】
输入数据的第一行包含一个正整数 n
.

接下来一行包含 n
 个正整数 a0,a1,...,an−1
.

之后一行包含一个正整数 q
,表示询问的个数。

之后 q 行,每行包含一个询问。格式如题目中所述。

【输出】
对于每个 QUERY 询问,在单独的一行中输出结果。如果不存在这样的 p,输出 no.

【输入样例】
10
1353600 5821200 10752000 1670400 3729600 6844320 12544000 117600 59400 640
10
MODIFY 7 20321280
QUERY 162343680
QUERY 1832232960000
MODIFY 0 92160
QUERY 1234567
QUERY 3989856000
QUERY 833018560
MODIFY 3 8600
MODIFY 5 5306112
QUERY 148900352
【输出样例】
6
0
no
2
8
8
【提示】
对于 100% 的数据,n≤100000,q≤10000,ai≤109(0≤i<n)
,QUERY x
 中的 x≤1018
,MODIFY id x 中的 0≤id<n,1≤x≤109
.

【来源】
没有写明来源
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int MAXN = 1e5+10;
int N, M;
int bl[1001], br[1001], pos[MAXN], block, num;
int Gcd[MAXN], Xor[MAXN];
int a[MAXN];
set<int>S[1001];
int gcd(int a, int b) {return b==0?a:gcd(b, a%b);}
void build(int t)
{
    S[t].clear();
    Gcd[bl[t]] = a[bl[t]]; Xor[bl[t]] = a[bl[t]];
    S[t].insert(Xor[bl[t]]);
    for(int i = bl[t]+1; i <= br[t]; i++){
        Gcd[i] = gcd(Gcd[i-1], a[i]);
        Xor[i] = Xor[i-1]^a[i];
        S[t].insert(Xor[i]);
    }
}

int main()
{
    int id, val;
    scanf("%d", &N);
    for(int i = 1; i <= N; i++){
        scanf("%d", &a[i]);
    }

    block = (int)sqrt(N);
    for(int i = 1; i <= N; i+=block){
        bl[++num] = i; br[num] = min(N,i+block-1);
        for(int j = bl[num]; j <= br[num]; j++)
            pos[j] = num;
    }

    for(int i = 1; i <= num; i++) build(i);

    char com[10];
    scanf("%d", &M);
    while(M--){
        scanf("%s", com);
        if(com[0] == 'M'){
            scanf("%d %d", &id, &val);
            a[++id] = val;
            build(pos[id]);
        }
        else{
            LL xx = 0;
            int Lxor = 0, Lgcd = 0;
            scanf("%lld", &xx);
            int flag = 0;
            for(int i = 1; i <= num; i++){
                int T = gcd(Lgcd, Gcd[br[i]]);
                if(T != Lgcd){
                    for(int j = bl[i]; j <= br[i]; j++)
                        if((LL)gcd(Lgcd, Gcd[j])*(LL)(Xor[j]^Lxor) == xx){
                            flag = j;
                            break;
                        }
                    if(flag) break;
                }
                else{
                    if(xx%T == 0 && S[i].count((int)(xx/T)^Lxor)){
                        for(int j = bl[i]; j <= br[i]; j++){
                            if((LL)gcd(Lgcd, Gcd[j])*(LL)(Xor[j]^Lxor) == xx){
                                flag = j;
                                break;
                            }
                        }
                    }
                    if(flag) break;
                }
                Lgcd = T; Lxor^=Xor[br[i]];
            }
            if(flag == 0) puts("no");
            else printf("%d\n", flag-1);
        }
    }
    return 0;
}
14029:[HEOI2015]定价

时间限制: 1000 ms         内存限制: 262144 KB
提交数: 22     通过数: 1
【题目描述】
 在市场上有很多商品的定价类似于 999 元、4999 元、8999 元这样。它们和 1000 元、5000 元和 9000 元并没有什么本质区别,但是在心理学上会让人感觉便宜很多,因此也是商家常用的价格策略。不过在你看来,这种价格十分荒谬。于是你如此计算一个价格 p(p 为正整数)的荒谬程度:

1、首先将 p 看做一个由数字组成的字符串(不带前导 0);

2、然后,如果 p 的最后一个字符是 0,就去掉它。重复这一过程,直到 p 的最后一个字符不是 0;

3、记 p 的长度为 a,如果此时 p 的最后一位是 5,则荒谬程度为 2 * a - 1;否则为 2 * a。

例如,850 的荒谬程度为 3,而 880 则为 4,9999 的荒谬程度为 8。

现在,你要出售一样闲置物品,你能接受的定价在 [L, R] 范围内,你想要给出一个荒谬度最低的价格。

【输入】
输入文件的第一行包含一个正整数 T,表示测试数据的数目。

每个测试数据占单独的一行,包含两个空格分隔的正整数 L, R,表示定价的区间。

【输出】
 对于每个测试数据,在单独的一行内输出结果。如果荒谬度最低的价格不唯一,输出最小的那个。

【输入样例】
3
998 1002
998 2002
4000 6000
【输出样例】
1000
1000
5000
【提示】
 对于 100% 的数据,T ≤ 100,1 ≤ L ≤ R ≤ 10^9.

【来源】
没有写明来源
#include <bits/stdc++.h>
using namespace std;
const int inf = 1e9+1;
struct node {
	int x, y;
	node(int _x=0, int _y=0):x(_x), y(_y){}
	inline node operator +(const node &t)const {
		if(x < t.x) return *this;
		if(x > t.x) return t;
		return node(x, min(y, t.y));
	}
};
node f[11][11][11][2];
bool vis[11][11][11][2];
int dl[11], dr[11];

inline node dfs(int len, int s, int cnt0, bool flg, bool fl, bool fr, int tmp) {
	if(!len) return node((flg ? 2*(s-cnt0)-1 : 2*(s-cnt0)), tmp);
	if(!fl && !fr && vis[len][s][cnt0][flg]) return f[len][s][cnt0][flg];
	node res = node(inf, inf);
	int mn = fl ? dl[len] : 0, mx = fr ? dr[len] : 9;
	for(int i = mn; i <= mx; ++i)
		res = res + dfs(len-1, s+(s||i), i?0:cnt0+1, i ? i==5: flg, fl&&i==mn, fr&&i==mx, tmp*10+i);
	if(!fl && !fr) {
		vis[len][s][cnt0][flg] = 1;
		f[len][s][cnt0][flg] = res;
	}
	return res;
}

inline int solve(int l, int r) {
	memset(dl, 0, sizeof dl); 
	memset(dr, 0, sizeof dr);
	int lenl = 0, lenr = 0;
	while(l) dl[++lenl] = l % 10, l /= 10;
	while(r) dr[++lenr] = r % 10, r /= 10;
	node res = dfs(lenr, 0, 0, 0, 1, 1, 0);
	return res.y;
}
int L, R;
int main () {
	int T;
	scanf("%d", &T);
	while(T--) {
		memset(vis, 0, sizeof vis); 
		scanf("%d%d", &L, &R);
		printf("%d\n", solve(L, R));
	}
}
14030:[HEOI2015]小L的白日梦

时间限制: 1000 ms         内存限制: 262144 KB
提交数: 3   通过数: 1
【题目描述】
在某一天,你有了一个女性朋友。

你打算利用k天时间陪她,每天有很多种娱乐方式可供选择,你需要从中选择一种进行(一天只能进行一个项目),比如说一起去看电影、一起去主题公园,一起去逛街等等,一共n种项目。当然每个项目重复太多次你都会觉得无聊,因此第i个项目最多进行c[i]次。你虽然智商很高,但是情商堪忧,即使这些你准备的活动都是希望让她开心的,不过由于你笨拙的语言表达和过于理智的行动,可能使这些活动出现意外。经过你悉心的计算,你发现如果某一天进行了第i个项目,如果一切顺利的话她应该是很高兴的,但她会有a[i]的概率不高兴。如果她本来是很高兴的,但突然今天你让她不高兴了,她就会觉得很失落,并且对你的好感度大大下降。你希望尽可能避免这种情况发生,因此你要安排这k天之内每天进行的项目,最小化她感到失落的期望次数。

你的女性朋友十分在意你,所以她的心情只会因为你发生改变。第一天之前,因为你没有邀请她进行任何活动,所以她是不高兴的。

【输入】
第一行有一个非负整数t,表示一共有t组数据。

对于每组数据,第一行有两个非负整数n,k,分别表示你准备的项目个数和你用来陪她的天数。(n<=10^5, k<=10^9)

接下来n行,每一行描述一个项目,形如“x[i]/y[i] c[i]”且三个数均为非负整数,表示进行完这个项目之后她有x[i]/y[i]的概率不高兴,并且这个项目只能进行不超过c[i]次。(x[i],y[i] <= 104,c[i] <= 10^9)

【输出】
一共t行,对于每组数据输出使她感到失落的最小期望次数,四舍五入保留6位小数。

【输入样例】
3
1 2
0/1 3
1 2
1/1 3
1 2
1/2 3
【输出样例】
0.000000
0.000000
0.250000
【提示】
【样例说明】

考虑第三组数据,因为只有一个项目所以只好每天都安排这个。

在第一天之前她总是不高兴的,一共有:

第一天不高兴,第二天也不高兴、

第一天高兴,第二天不高兴、

第一天不高兴,第二天高兴、

第一天不高兴,第二天也不高兴,

这四种情况,又因为每天的项目让她高兴或者是不高兴的概率都是0.5,因此这四种情况是等概率发生的。

只有在第二种情况下,她会感到失落一次。

因此答案是(1*1+0*3)/4=0.25.

对于100%的数据,n<=105,k<=109,数据组数不会太多,大概不超过10组,数据保证分数有意义并且∑c[i]>=k。

【来源】
没有写明来源
#include <bits/stdc++.h>
using namespace std;
typedef long long lng;
typedef long double ldb;
struct data {
    int cnt; ldb val;
    inline data(void) {};
    inline data(int a, ldb b)
        : cnt(a), val(b) {};
    inline void read(void) {
        static int a, b;
        scanf("%d/%d", &a, &b);
        val = (ldb)a / b;
        scanf("%d", &cnt);
    }
}A[150000], B[350000];
inline bool operator < (const data &a, const data &b) {
    return a.val > b.val;
}
int n, m, cas, tot;
inline ldb calc(void) {
    ldb ret = 1E18, sum = 0;
    lng now = 1, rem = m;
    for (int i = n; i; --i)
        sum += (B[i].cnt - 1) * B[i].val * (1 - B[i].val) + (1 - B[i].val) * B[i + 1].val, rem -= B[i].cnt;
    for (int i = 1; i <= n; ++i ) {
        rem -= B[i].cnt;
        while (now <= n && rem <= 0)
            sum -= (B[now].cnt - 1) * B[now].val * (1 - B[now].val) + (1 - B[now].val) * B[now + 1].val, rem += B[now++].cnt;
        if (rem <= 0)break;
        sum += (B[i].cnt - 1) * B[i].val * (1 - B[i].val) + (1 - B[i - 1].val) * B[i].val;
        ret = min(ret, sum + (rem - 1) * B[now - 1].val * (1 - B[now - 1].val) + (1 - B[now - 1].val) * B[now].val + (1 - B[i].val) * B[now - 1].val);
    }
    rem = m, sum = 0;
    for (int i = 1; i <= n; ++i) {
        int mn = min(rem, (lng)B[i].cnt);
        if(!mn)break; else rem -= mn, sum += (mn - 1) * B[i].val * (1 - B[i].val) + (1 - B[i - 1].val) * B[i].val;
    }
    return ret = min(ret, sum);
}
signed main(void) {
    for (scanf("%d", &cas); cas--; ) {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; ++i) {
            A[i].read(); if (!A[i].cnt)--i, --n;
        }
        sort(A + 1, A + n + 1); tot = 0;
        for (int i = 1; i <= n; ++i) {
            B[++tot] = data(1, A[i].val);
            if (--A[i].cnt) {
                if (A[i].cnt > 1)
                    B[++tot] = data(A[i].cnt - 1, A[i].val);
                B[++tot] = data(1, A[i].val);
            }
        }
        B[0].val = 1, B[(n = tot) + 1].val = 0;
        ldb ans = calc();
        for (int i = 1; i <= n; ++i)
            if (i < n + 1 - i)swap(B[i], B[n + 1 - i]);
        for (int i = 1; i <= n; ++i)B[i].val = 1 - B[i].val;
        ans = min(ans, calc());
        printf("%.6lf\n", (double)fabs(ans));
    }
}

都是毒瘤,没想到我散发欧皇气息硬是一次性过了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值