C:
题意:保留q条树枝,求最大能够保留多少苹果。
思路:树上背包dp,dp[i][j] 为以i为根保留j个分支
dp[i][j] = max( dp[i][j-t] + dp[v][t] + w[i][v] , dp[i][j] )
v为 i 子节点,w[i][v]为i到v的苹果数。
Code:
#include <bits/stdc++.h>
#define mem(a,x) memset( a , 0 , sizeof(a) );
#define LL long long
using namespace std;
typedef pair<int,int> P;
const int AX = 105;
int dp[AX][AX];
int n , q ;
std::vector<P> G[AX];
//dp[i][j] = max( dp[i][j-t] + dp[v][t] + w[i][v] , dp[i][j] )
void dfs( int u , int f ){
for( int i = 0 ; i < G[u].size() ; i++ ){
P tmp = G[u][i];
int v = tmp.first;
int c = tmp.second;
if( v == f ) continue;
dfs( v , u ) ;
for( int j = q + 1 ; j > 1 ; j -- ){
for( int t = 1 ; t < j ; t ++ ){
dp[u][j] = max( dp[u][j-t] + dp[v][t] + c , dp[u][j] );
}
}
}
}
int main(){
int T;
scanf("%d",&T);
while( T-- ){
scanf("%d%d",&n,&q);
for( int i = 0 ; i <= n ; i++ ){
G[i].clear();
}
mem( dp , 0 ) ;
int u , v , w ;
for( int i = 0 ; i < n - 1 ; i++ ){
scanf("%d%d%d",&u,&v,&w);
G[u].push_back(P(v,w));
G[v].push_back(P(u,w));
}
dfs( 1 , -1 );
printf("%d\n",dp[1][q+1]);
}
return 0 ;
}
F:
题意:
给n个数,问1-m里面有多少个是这n个数任意个的倍数。
思路:由容斥原理我们知道结果是,m范围内:一个数的倍数 - 两个数最小公倍数的倍数+3个数。。。
总之就是奇数个的时候是加号,偶数个的时候是减号。
然后n只有15个,可以用二进制表示其用到哪些个数。
每次用奇数个时结果加上,偶数个时结果减去,用公式写就是:
Σ(1<=i<=n) m / A[i] - Σ ( i , j ∈ (1,n) ) m / lcm( A[i], A[j] ) + ….-…..
Code:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int AX = 32768+66;
LL A[AX];
LL gcd( LL a, LL b) {
return b ? gcd(b, a % b) : a;
}
int main() {
int n;
LL m;
int T;
scanf("%d",&T);
while ( T-- ){
scanf("%d%lld", &n, &m);
for (int i = 0; i < n; ++i) {
scanf("%lld", &A[i]);
}
LL ans = 0;
for (int i = 1; i < (1 << n); ++i) {
LL d = 1;
int use = 0;
for (int j = 0; j < n; ++j) {
if (i & (1 << j)) {
++use;
d = (LL)d / gcd(A[j], d) * A[j];
}
}
if (use & 1) {
ans += m / d;
}
else {
ans -= m / d;
}
}
printf("%lld\n", ans);
}
return 0;
}
J
题意:计算公式
思路:公式等于 2^(n - 2 ) * n * ( n + 1 )
Code:
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int AX = 2e3+66;
const int MOD = 1e9+7;
int a[AX];
LL quick( LL a, LL b ){
LL ans = 1LL;
while( b ){
if( b & 1 ){
ans = ( ans * a) % MOD;
}
b >>= 1 ;
a = ( a * a ) % MOD;
}
return ans % MOD;
}
int main(){
int T;
scanf("%d",&T);
while( T-- ){
LL n ;
scanf("%lld",&n);
LL res = n * ( n + 1 ) % MOD * quick( 2 , n - 2 ) % MOD;
printf("%lld\n",res%MOD);
}
return 0 ;
}
K
题意:给出n个二维坐标系上的点,问应该取哪一点作为定点,其他点到这个点的曼哈顿距离之和最小。
思路:先对x从小到大排序,x左边的坐标到这个点的x距离就是x*num - sumx.
其中num是x左边点的个数,sumx是左边点的横坐标之和。
x右边的坐标到这个点的距离和就是 总共的x坐标和 减去 x左边的x坐标的和, 再减去 右边点的个数 * x的横坐标。
对y同理。
最后将每个点的x+y取最小值即可。
另外,坐标有负值,为了计算方便,我都加上1e9.
Code:
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int AX = 2e5+66;
const int base = 1e9;
struct Node{
LL x , y ;
int id ;
}a[AX];
LL sum_x[AX];
LL sum_y[AX];
LL xx[AX];
LL yy[AX];
bool cmpx( const Node &a , const Node &b ){
return a.x < b.x;
}
bool cmpy( const Node &a , const Node &b ){
return a.y < b.y;
}
int main(){
int n ;
scanf("%d",&n);
LL sumx = 0LL;
LL sumy = 0LL;
for( int i = 1 ; i <= n ; i++ ){
scanf("%lld%lld",&a[i].x,&a[i].y);
a[i].x += base;
a[i].y += base;
a[i].id = i ;
sumx += a[i].x;
sumy += a[i].y;
}
sort( a + 1 , a + n + 1 , cmpx );
for( int i = 1 ; i <= n ; i++ ){
sum_x[i] = sum_x[i-1] + a[i].x;
}
for( int i = 1 ; i <= n ; i++ ){
LL x = a[i].x ;
int id = a[i].id ;
LL left = (LL)( i - 1 ) * x - sum_x[i-1];
LL right = sumx - sum_x[i] - (LL)( n - i ) * x;
xx[id] = left + right;
}
sort( a + 1 , a + n + 1 , cmpy );
for( int i = 1 ; i <= n ; i++ ){
sum_y[i] = sum_y[i-1] + a[i].y;
}
for( int i = 1 ; i <= n ; i++ ){
LL y = a[i].y ;
int id = a[i].id ;
LL up = (LL)( i - 1 ) * y - sum_y[i-1];
LL down = sumy - sum_y[i] - (LL)( n - i ) * y;
yy[id] = up + down;
}
LL res = xx[1] + yy[1];
for( int i = 2 ; i <= n ; i++ ){
res = min( res , xx[i] + yy[i] );
//cout << res << endl;
}
printf("%lld\n",res);
return 0 ;
}