18.8.13 考试总结
1.1 问题描述
请构造一颗n 个节点的树,使得其价值最大。
f(d) 表示树上,度数为d 的一个点能够获取的价值。
这棵树的价值为
Σn
i=1 f(di)
di 表示第i 个点的度数
1.2 输入
第一行一个整数T,接下来T 组数据,每组数据输入两行。
第一行输入整数n。第二行输入n 1 个整数f(i) 代表f(1) f(n 1)。
1.3 输出
对于每组数据输出一行,为能够构造的树的最大价值。
一开始我以为是一道树形dp...
考完了才知道原来这个是一道背包问题
n个节点 总共有2n - 2个度数 因为这是一棵树 所以每个节点至少有一个度
关于这道题有一个性质 就是只要每个节点至少有一个度 那么不管剩下的度数如何分配
只要分完了 那么绝对可以连接成一棵树
所以就对剩下的n - 2个度数做背包就好了 初值就是n * 度数为一个的点获得的收益
代码
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int T,n,f[N],dp[N]; int main( ) { freopen("a.in","r",stdin); freopen("a.out","w",stdout); scanf("%d",& T); while(T --) { scanf("%d",& n); for(int i = 1;i < n;i ++) scanf("%d",& f[i]); dp[0] = n * f[1]; for(int i = 1;i <= n - 2;i ++) for(int j = 0;j <= i;j ++) { dp[i] = max(dp[i],dp[j] + f[i - j + 1] - f[1]); } printf("%d\n",dp[n - 2]); for(int i = 1;i <= n - 2;i ++) dp[i] = 0; } return 0; }
2.1 问题描述
给出正整数n 和k,计算j(n; k) =Σni=1 k mod i 的值,其中k mod i 表示k 除以i 的余
数。例如j(5; 3) = 3 mod 1 + 3 mod 2 + 3 mod 3 + 3 mod 4 + 3 mod 5 = 0 + 1 + 0 + 3 + 3 = 7
2.2 输入
输入两个整数n; k。
2.3 输出
输出一个整数结果。
这个我考试的时候就做出来了结果空间开爆了 无语
就是一个对于模数分块的问题 把商一样的魔术分成一块 就降低复杂度到需要的就可以了
代码
#include <bits/stdc++.h> typedef long long ll; using namespace std; const int N = 1e7 + 5; ll n,k,shang[N],ans = 0,lf,rg; int dep = 0; struct beishu { ll l,r,tim; }kuai[200]; int main( ) { freopen("b.in","r",stdin); freopen("b.out","w",stdout); scanf("%I64d%I64d",& n,& k); ll s = k; while(s) { dep ++; s /= 10; } if(dep <= 7) { for(int i = 1;i <= min(n,k);i ++) { shang[i] = k / i; } for(int i = 1;i <= min(n,k);i ++) { ans += k - shang[i] * i; } if(n > k) { ans += (n - k) * k; } } else { bool tag = false; ll cut = 2; int num = 0; for(int i = 100;i >= 1;i --) { lf = k / (i + 1) + 1; rg = k / i; if(rg > n || lf > n) { tag = true; cut = i; break; } kuai[++ num].l = lf; kuai[num].r = rg; kuai[num].tim = i; } for(int i = 1;i <= min(k / 101,n);i ++) { shang[i] = k / i; } for(int i = 1;i <= min(k / 101,n);i ++) { ans += k - shang[i] * i; } if(num > 0) { for(int i = 1;i <= num;i ++) { ll timm = kuai[i].r - kuai[i].l + 1; ans += k * timm - kuai[i].tim * (kuai[i].l + kuai[i].r) * timm / 2; } if(tag) { ll lf = kuai[num].r + 1; ll tim = kuai[num].tim - 1; ll timm = n - lf + 1; ans += k * timm - tim * (lf + n) * timm / 2; } } if(n > k) { ans += (n - k) * k; } } printf("%I64d",ans); return 0; }
3.1 问题描述
n 个霍比特人打算在佛罗多的家里过夜。佛罗多有n 张床位和m 个枕头(n m)。n 张床
位排成一列。每个霍比特人需要一张床和至少一个枕头睡觉,但是,每个人都想要尽可能多的
枕头。当然,并不总是可以平等地分享枕头,但如果霍比特人比他邻居少至少两个枕头,那么
他会受伤。
佛罗多将睡在排在第k 个的床上。他可以拥有多少枕头,以便每个霍比特人至少有一个枕
头,每个枕头都被给予霍比特人,而且没有人受伤?
3.2 输入
第一行一个整数T,接下来T 组数据。
每组数据输入一行,三个整数n; m; k。
3.3 输出
对于每组数据一行,输出佛罗多最多拥有多少枕头。
这道题真的非常简单了 但是考试的时候 我只考虑了放一边枕头没办法放完
忘记了他两边都有可能放不下
就是一个sd二分
代码
#include <bits/stdc++.h> typedef long long ll; using namespace std; int T; ll n,m,k,s[100005],ans; bool check(ll num) { ll t1,t2; if(num > k) { t1 = (num - k + 1 + num) * k / 2; } else { t1 = (num + 1) * num / 2; t1 += k - num; } if(num > n - k + 1) { t2 = (num - n + k + num) * (n - k + 1) /2; } else { t2 = (num + 1) * num / 2; t2 += n - k + 1 - num; } if(t1 + t2 - num <= m) return true; return false; } ll solve(ll l,ll r) { ll ans = 0; while(l <= r) { ll mid = (l + r) >> 1; if(check(mid)) { ans = mid; l = mid + 1; } else { r = mid - 1; } } return ans; } int main( ) { freopen("c.in","r",stdin); freopen("c.out","w",stdout); scanf("%d",& T); while(T --) { scanf("%I64d%I64d%I64d",& n,& m,& k); ans = solve(1,m); printf("%I64d\n",ans); } return 0; }
总之还是不够细心 蒟蒻