7-28 ACM训练联盟周赛解题报告 C树上dp ,F容斥原理 J组合数学公式 K贪心(曼哈顿距离和)

43 篇文章 1 订阅
16 篇文章 0 订阅

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 ; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值