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;
}

总之还是不够细心 蒟蒻

posted @ 2018-08-13 19:21  阿澈说他也想好好学习  阅读(165)  评论(0编辑  收藏  举报