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