51nod-算法马拉松19

1674 区间的价值 V2(传送门)

解题思路

拆位运算,简单的所就是一位一位计算贡献就可以了

代码

#include 
#include 
#include 


typedef long long LL;
using namespace std;

const int SIZE = 30 + 2;
const int MAXN = 1e5 + 5;
const int mod = 1e9 + 7;
int A[MAXN];
int n, p[SIZE];

int main(){
    while(~scanf("%d", &n)){
        int Max = 0;
        for(int i = 1;i <= n;i ++){
            scanf("%d", &A[i]);
            Max = max(Max, A[i]);
        }
        LL ans = 0;
        for(int i = 0;i < SIZE;i ++){//and
            LL sum = 0, last = 0;
            memset(p, 0, sizeof(p));
            for(int j = 1;j <= n;j ++){
                if(A[j] >> i & 1LL){
                    for(int k = 0;k < SIZE;k ++){//or
                        if(A[j] >> k & 1LL){
                            p[k] = j;
                        }
                        sum = (sum + (1LL << k) * (p[k] - last)) % mod;
                    }
                }
                else{
                    for(int k = 0;k < SIZE;k ++){
                        p[k] = j;
                    }
                    last = j;
                }
            }
            ans = (ans + sum * (1LL << i)) % mod;
        }
        printf("%I64d\n", ans);
    }
    return 0;
}

1616 最小集合(传送门)

解题思路

对于是否存在 x 一个数,只需要 gcd(a1,a2,a3,...) ,其中 an ,表示为 x 的倍数,当所有 x 的倍数的最大公约数等于 x 的时候表示 x 这个数一定存在在这个集合中

代码

#include 
#include 
#include 


#define FIN freopen("input.txt", "r", stdin)
using namespace std;
const int MAXN = 1e5 + 5;
const int MAXM = 1e6 + 5;
int n;
int A[MAXN];
bool vis[MAXM];

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

int main(){
    //FIN;
    while(~scanf("%d", &n)){
        int Max = 0, ans = 0;
        memset(vis, false, sizeof(vis));
        for(int i = 0;i < n;i ++){
            scanf("%d", &A[i]);
            if(!vis[A[i]]) ans ++;
            vis[A[i]] = true;
            Max = max(Max, A[i]);
        }
        //printf("[%d]\n", Max);
        for(int i = 1;i <= Max;i ++){
            if(vis[i]) continue;
            int w = -1;
            for(int j = i;j <= Max;j += i){
                if(!vis[j]) continue;
                if (w == -1) w = j;
                w = gcd(w, j);
            }
            if(w == i) {
                ans ++;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

1622 集合对(传送门)

解题思想

直接看成异或,

(PxorA)xor(QxorB)=AxorB

(PxorQ)xor(AxorB)=AxorB

两边乘以 AxorB
(PxorQ)=

PQPQ=

所以 P=Q ,又因为 PA QB ,所以我们就是在 AB 中选择子集,这样就变成了对于 AB 中的元素选或者不选两种情况,就是 2|AB|

代码

#include
#include
#include
using namespace std;

typedef long long ll;
const ll p = 1000000007;

int main() {
    ll A, B, C;
    scanf("%lld%lld%lld", &A, &B, &C);
    ll t = 2, ans = 1;
    while (C) {
        if (C & 1) ans = (ans * t) % p;
        t = (t * t) % p;
        C >>= 1;
    }
    printf("%lld\n", ans);
    return 0;
}

石头剪刀布威力加强版(传送门)

解题思路

这道题目思维一直纠结

官方题解

为了方便起见
我们令 n<m n,m 互质(若n,m不互质可以分为多个若干子问题)
假设有nm局,那么每一对都打过,因此可以将这些先统计出来,k变成一个不大于nm的数
这里,如果n,m不互质,那么并不是每一对都会打,例如n=4,m=6则小A的第一个数和小B的第2个数是无论如何都不会打的。
令小A的4个数编号为1,2,3,4,小B的6个数编号为1,2,3,4,5,6。
那么可以分成的两个子问题为。
小A的1,3与小B的1,3,5以及小A的2,4与小B的2,4,6打。这样不断变为若干子问题,直到最后n,m互质。

接下来,我们考虑如果打了k局,那么对于前k%n个数都打了k/n+1局,后面的数都打了k/n局,我们可以先将前k%n个数的第k/n+1局单独拎出来,累加进答案里。问题转换成对于每个数,都要打k/n局。
我们考虑对于第一个数,每次与它打的位置依次是多少,即
1,n%m+1,2n%m+1,3n%m+1….
可以观察到,这个序列实际上是长度为m的循环序列。
因此对于1~n的每个数,只要在这个序列中找到最先出现的,接下来连续k/n个数即为与它打的位置,这里可以通过前缀和O(1)更新答案。
这样就可以在O(m)线性时间内找出答案。

代码

待补…太麻烦啦

你可能感兴趣的:(ACM-算法马拉松)