【8.12校内测试】【容斥原理】【数位DP(二进制)】【压维(?)】

emm,今天测试没有什么感想。。

1 a

1.1 问题描述

有n 个青蛙,m 个石头围成一圈编号为0  m��1,第i 只青蛙每次跳ai 步,这意味着青
蛙能从石头j mod m 跳到石头(j + ai) mod m。青蛙每跳一个石头,就占领它。每只青蛙最开
始在0 号石头,它们可以一直跳下去。这些青蛙最后占领的石头编号和为多少?

1.2 输入

第一行一个整数T,接下来T 组数据,每组数据输入两行。
第一行输入两个整数n;m。第二行输入n 个整数ai。

1.3 输出

对于每组数据输出一行,形如”Case[空格][井号]X:[空格]Y”。X 为数据编号,从1 开始,Y
为占领的石头编号和。(排版有问题)

1.4 输入输出样例1

1.4.1 输入样例

3
2 12
9 10
3 60
22 33 66
9 96
81 40 48 32 64 16 96 42 72

1.4.2 输出样例

Case ♯1: 42
Case ♯2: 1170
Case ♯3: 1872

1.5 约定和数据范围

对于30% 的数据, n  10, m  1e3,
对于100% 的数据, 1  T  50, 1  n  1e4, 1  m  1e9, 1  ai  1e9。

把所有的跳跃长度处理出来(就是gcd(m,a[i]),去算贡献的时候容斥,记录当前出现的次数和应该出现的次数
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define ll long long
using namespace std;

ll m, a[10005]; 
int n, flag[10005], ti;
int G[10005], tot, sum, appear[10005], cnt[10005], qwq[10005];

int gcd ( int a, int b ) {
    return b == 0 ? a : gcd ( b, a % b );
}

void init ( int x ) {
    memset ( qwq, 0, sizeof ( qwq ) );
    memset ( appear, 0, sizeof ( appear ) );
    for ( int i = 1; i * i <= x; i ++ ) {
        if ( x % i == 0 ) {
            qwq[++sum] = i;
            if ( i * i != x ) qwq[++sum] = x/i;
        }
    }
    if ( x != 1 ) qwq[++sum] = x;////m分解因数,表示可能做出贡献的跳跃长度,m不算
}

int main ( ) {
    freopen ( "a.in", "r", stdin );
    freopen ( "a.out", "w", stdout );
    int T;
    scanf ( "%d", &T );
    while ( T -- ) {
        memset ( cnt, 0, sizeof ( cnt ) );
        memset ( flag, 0, sizeof ( flag ) );
        memset ( G, 0, sizeof ( G ) );
        sum = 0;
        scanf ( "%d%I64d", &n, &m );
        for ( int i = 1; i <= n; i ++ )
            scanf ( "%I64d", &a[i] );
        init ( m );
        sort ( qwq + 1, qwq + 1 + sum );
        sum --;
        for ( int i = 1; i <= n; i ++ ) {
            int d = gcd ( m, a[i] );
            for ( int j = 1; j <= sum; j ++ )
                if ( qwq[j] % d == 0 ) appear[j] = 1;//应该出现的次数
        }
        ll ans = 0;
        for ( int i = 1; i <= sum; i ++ ) {
            if ( cnt[i] != appear[i] ) {//cnt是当前已经出现的次数
                ll tmp = ( m - 1 ) / qwq[i];
                ans += tmp * ( tmp + 1 ) / 2 * qwq[i] * ( appear[i] - cnt[i] );//多了减少了加
                for ( int j = i + 1; j <= sum; j ++ )
                    if ( qwq[j] % qwq[i] == 0 ) cnt[j] += appear[i] - cnt[i];//更新已经出现的次数
            }
        }
        printf ( "Case #%d: %I64d\n", ++ti, ans );
    }
    return 0;
}

2 b

2.1 问题描述

对于数字n, [1; n] 内,有多少个数可以被由它转化成的二进制里面的1 的个数整除
比如9 的二进制形式为1001,但是9%2! = 0,所以它不是
比如8 的二进制形式为1000,8%1 == 0,所以它是
如对于9 来说[1; 9] 内有5 个数字是合法的

2.2 输入

输入一个整数n

2.3 输出

输出一个整数结果

2.4 输入输出样例1

2.4.1 输入样例

153

2.4.2 输出样例

42

2.5 约定和数据范围

对于20% 的数据, n  50000000,
对于100% 的数据, 1  n  1e19。

将所有都统一成二进制数,做数位dp即可。 【注意】1e19需要用unsigned long long,此时不能memset成-1,记录vis数组表示当前状态是否在当前模数中被计算过(变化过的记忆化
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll unsigned long long
using namespace std;

ll n;
ll dp[70][70][70][2], fig[20], vis[70][70][70][2];

ll dfs ( int dep, int up, int mod, int num, ll sum, int pre_num ) {
    if ( vis[dep][num][mod][up] == pre_num ) return dp[dep][num][mod][up];
    if ( !dep && mod == 0 && num == pre_num ) return 1;
    if ( !dep ) return 0;
    int cnt = up ? fig[dep] : 1;
    ll ans = 0;
    for ( int i = 0; i <= cnt; i ++ ) {
        ans += dfs ( dep - 1, up && i == cnt, ( sum * 2 % pre_num + i ) % pre_num, num + i, sum * 2 + i, pre_num );
    }
    vis[dep][num][mod][up] = pre_num;
    dp[dep][num][mod][up] = ans;
    return ans;
}

ll work ( ll n ) {
    int ee = 0;
    while ( n ) {
        fig[++ee] = n % 2;
        n /= 2;
    }
    ll ans = 0;
    for ( int i = 1; i <= ee; i ++ ) {//枚举1的个数
        ans += dfs ( ee, 1, 0, 0, 0, i );
    }
    return ans;
}

int main ( ) {
    freopen ( "b.in", "r", stdin );
    freopen ( "b.out", "w", stdout );
    scanf ( "%I64d", &n );
    ll ans = work ( n );
    printf ( "%I64d", ans );
    return 0;
}

3 c

3.1 问题描述

有一个n  m 的矩阵,左上角为(1; 1) 右下角为(n;m),每个格子边长为4,每个格子中
央有权值为aij。对于一个格子的顶点,这个点的权值是
i = 1 n j = 1 m d i s 2 a i j , dis 是这个点到
格子(i; j) 中心的欧几里得距离。求该权值最小的顶点。如有相同权值的顶点,首先选择x 坐
标较小的顶点,如果还有相同,再选择y 坐标较小的顶点。
这里写图片描述

3.2 输入

第一行为两个整数n;m,
接下来n 行,每行m 个整数,按图示从左到右,从上到下,表示aij。

3.3 输出

输出两行,第一行一个整数为最优点的最小权值和为多少,第二行两个整数分别表示最优
点的x 和y 坐标。

3.4 输入输出样例1

3.4.1 输入样例

2 3
3 4 5
3 9 1
3

3.4.2 输出样例

392
1 1

3.5 输入输出样例2
3.5.1 输入样例

3 4
1 0 0 0
0 0 3 0
0 0 5 5

3.5.2 输出样例

240
2 3

3.6 约定和数据范围

对于20% 的数据, n or m = 1,
对于100% 的数据, 1  n;m  1000, 1  aij  100000。

可以发现,可以把贡献拆成行和列分开计算,同一列上所有点在行上受到的其他各列的总贡献是一样的,行同理,最后每个点的总价值把行和列加起来即可。【注意】点和格子的坐标
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;

int n, m;

ll a[1005][1005], line[1005], row[1005], row_sum[1005];
ll line_sum[1005];

int main ( ) {
    freopen ( "c.in", "r", stdin );
    freopen ( "c.out", "w", stdout );
    scanf ( "%d%d", &n, &m );
    for ( int i = 1; i <= n; i ++ )
        for ( int j = 1; j <= m; j ++ ) {
            scanf ( "%I64d", &a[i][j] );
            line_sum[j] += a[i][j];
            row_sum[i] += a[i][j];
        }
    for ( int i = 0; i <= m; i ++ ) 
        for ( int j = 1; j <= m; j ++ ) {
            if ( j <= i ) {
                line[i] += line_sum[j] * ( ( i - j ) * 4 + 2 ) * ( ( i - j ) * 4 + 2 );
            } else {
                line[i] += line_sum[j] * ( ( j - i - 1 ) * 4 + 2 ) * ( ( j - i - 1 ) * 4 + 2 );
            }
        }
    for ( int i = 0; i <= n; i ++ ) 
        for ( int j = 1; j <= n; j ++ ) {
            if ( j <= i ) {
                row[i] += row_sum[j] * ( ( i - j ) * 4 + 2 ) * ( ( i - j ) * 4 + 2 );
            } else {
                row[i] += row_sum[j] * ( ( j - i - 1 ) * 4 + 2 ) * ( ( j - i - 1 ) * 4 + 2 );
            }
        }
    ll ans = 1e18;
    int x, y;
    for ( int i = 0; i <= n; i ++ )
        for ( int j = 0; j <= m; j ++ ) {
            ll tmp = line[j] + row[i];
            if ( ans > tmp ) {
                x = i; y = j;
                ans = tmp;
            }
        }
    printf ( "%I64d\n%d %d", ans, x, y );
    return 0;
}

猜你喜欢

转载自blog.csdn.net/wans__/article/details/81608105
今日推荐