【8.13校内测试】【DP】【按除数分类】【二分】

感觉今天状态不太好啊一大早就很困,t1卡得有点久,以为三道题都是这个难度,结果难度完全是倒着排的啊!!在dp和数学上还得多练题!!

很像背包的一道DP??先不考虑树的结构,给每个点都先分配一个度数,剩下n-2个度数DP分配,dp[i]表示分配i个点出去可以获得的最大价值,由dp[1]~dp[i-1]转移过来(相当于在所有状态中选择最佳)。可以保证只要剩下n-2个度数分配出去就一定可以成立。

DP有关的题码量真是很少啊...

 

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;

int n, a[2020];
ll dp[2020];

int main ( ) {
    freopen ( "a.in", "r", stdin );
    freopen ( "a.out", "w", stdout );
    int T;
    scanf ( "%d", &T );
    while ( T -- ) {
        memset ( dp, 0, sizeof ( dp ) );
        scanf ( "%d", &n );
        for ( int i = 1; i < n; i ++ )
            scanf ( "%d", &a[i] );
        dp[0] = a[1] * n;
        for ( int i = 1; i <= n-2; i ++ )
            for ( int j = 0; j < i; j ++ )
                dp[i] = max ( dp[i], dp[j] - a[1] + a[i-j+1] );
        printf ( "%I64d\n", dp[n-2] );
    }
    return 0;
}

 

稍微一推就可以发现,k=k/i*i+k%i(p为余数,k/i向下取整),所以k%i=k-k/i*i,因此可以把所有k/i向下取整相同的i分到一组,i在这段中又可以用等差数列算出来。也能够证明枚举的k/i不超过sqrt(k)个,时间复杂度大大减少。

数学类的想通了码量什么的???

 

#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;

ll n, k;

int main ( ) {
    freopen ( "b.in", "r", stdin );
    freopen ( "b.out", "w", stdout );
    scanf ( "%I64d%I64d", &n, &k );
    ll ki = 1;
    ll ans = n * k;
    while ( ki <= min ( n, k ) ) {
        ll a = k / ki;
        ll b = min ( k / a, n );
        ans -= a * ( ki + b ) * ( b - ki + 1 ) / 2;
        ki = b + 1;
    }
    printf ( "%I64d", ans );
    return 0;
}

 

 

 

 

 

今天真正的水题!一眼就能看出二分答案啊!主要是check函数里面要注意,等差数列如果小的值到0了要把之后的全都变成1再判断。

 

#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;

ll n, m, k;

bool check ( ll an ) {
    ll t1 = 0, t2 = 0;
    if ( an >= k ) t1 = ( an - k + 1 + an - 1 ) * ( k - 1 ) / 2;
    else {
        t1 = ( an - 1 ) * an / 2;
        t1 += ( k - an );
    }
    if ( n - k + 1 <= an ) t2 = ( an - n + k + an ) * ( n - k + 1 ) / 2;
    else {
        t2 = ( an ) * ( an + 1 ) / 2;
        t2 += n - ( k + an - 1 );
    }
    if ( t1 + t2 <= m ) return 1;
    return 0;
}

ll erfen ( ) {
    ll l = 1, r = m, res = 0;
    while ( l <= r ) {
        int mid = ( l + r ) >> 1;
        if ( check ( mid ) ) {
            l = mid + 1; res = mid;
        } else r = mid - 1;
    }
    return res;
}

int main ( ) {
    freopen ( "c.in", "r", stdin );
    freopen ( "c.out", "w", stdout );
    int T;
    scanf ( "%d", &T );
    while ( T -- ) {
        ll ans = 0;
        scanf ( "%I64d%I64d%I64d", &n, &m, &k );
        if ( n == m ) {
            printf ( "1\n" ); continue;
        }
        ans = erfen ( );
        printf ( "%I64d\n", ans );
    }
    return 0;
}

 

posted @ 2018-08-13 20:33  Wans_ovo  阅读(174)  评论(0编辑  收藏  举报