ACM训练日记—4月19日

时间:2022-09-07 13:51:40

        还是整理下题目,回顾下见过的有启发的题吧。

 回忆:a^n = a^(n%(m-1)) (%m)

hdoj 4810 Wall Painting

题意:给定n个数,进行n次操作。对第i次操作,要求你从n个数里面任选i个数,用这i个数异或得到一个新数,并把所有新数相加(可以重复加)。对每次操作,输出新数相加之和。

 来自:https://blog.csdn.net/qingshui23/article/details/52599936

解题思路:
首先将每一个数的二进制求出来,在每一位二进制数中求出 1 的个数,拿样例来说吧。
1  =  0   0   0   1
2  =  0   0   1   0
10=  1   0   1   0 
1  =  0   0   0   1
     每次选奇数个 1 和 剩余的选的个数-1 个 00, 比如说求任意两个数的异或和,那么我们选 1个 1 ,选 2−1 个 0, 我们肯定选奇数个 1, 因为偶数个 1 异或还是 0 没有意义,然后就是组合数了, 从当前的 1 的总个数里面选择奇数个 1 ,在乘以相应的 2^x次幂,(c[cnt[j]][k]∗c[(n−cnt[j])][i−k])∗(2j)其中 cnt[j] 表示的是第 j 位 11 的总个数, k表示的是要选择的 1 的个数。i表示的

      我的理解就是按位分开讨论加和,讨论出不为0的情况值加和。

代码来自:https://blog.csdn.net/chenzhenyu123456/article/details/49819051

int num[40];  
LL C[1010][1010];  
void getC()  
{  
    for(int i = 0; i <= 1000; i++)  
        C[i][0] = 1;  
    for(int i = 1; i <= 1000; i++)  
        for(int j = 1; j <= i; j++)  
            C[i][j] = (C[i-1][j] + C[i-1][j-1]) % MOD;  
}  
int main()  
{  
    getC();  
    int n;  
    while(Ri(n) != EOF)  
    {  
        CLR(num, 0);  
        for(int i = 1; i <= n; i++)  
        {  
            LL a; Rl(a);  
            for(int j = 0; j <= 35; j++)  
                if(a & (1LL<<j))  
                    num[j]++;  
        }  
        for(int i = 1; i <= n; i++)  
        {  
            LL cnt = 0;  
            for(int j = 0; j <= 35; j++)//第j位的1  
            {  
                if(num[j] == 0) continue;  
                for(int k = 1; k <= i && k <= num[j]; k+=2)//选k个1 k为奇数时异或才加  
                {  
                    if(n-num[j] >= i-k)  
                    {  
                        cnt += C[num[j]][k] * ((1LL<<j) % MOD) % MOD * C[n-num[j]][i-k] % MOD;  
                        cnt %= MOD;  
                    }  
                }  
            }  
            if(i > 1)  
                printf(" ");  
            printf("%lld", cnt);  
        }  
        printf("\n");  
    }  
    return 0;  
}  

Codeforces 615D Multipliers

题意:给定m个质因子p[],有p[1]*p[2]*...*p[m] == n,问n所有因子的乘积 % 1e9 + 7。

来自:https://blog.csdn.net/chenzhenyu123456/article/details/50486219

思路:组合数学。
先把所有因子存起来,并统计个数为top(不相同的)。考虑每个质因子rec[i]做出的贡献,结果为rec[i]^num。其中num为rec[i]在n所有因子乘积中存在的个数。定义1-i质因子的组合方案数l[i],后i-top个质因子的组合方案数r[i]。
考虑第i个质因子的状态,取或不取,不取有l[i-1],取则有l[i-1] * cnt[rec[i]](可能取1 - cnt[rec[i]])同理求法r[]。
对于一个质因子rec[i],可以与前、后组合,那么它的可组合方案数为temp = l[i-1] * r[i+1]。若该质因子有Num个,则有方案rec[i] * temp 、rec[i] * rec[i] * temp... rec[i]^(Num) * temp。统计rec[i]出现次数有num = (Num+1) * Num/2 * temp.下面ans *= rec[i] ^ num就好了。

 num值很大,费马小定理a^n = a^(n%(m-1)) (%m)搞下就ok了。也就是说num % (1e9+7-1)。

     还能说什么,膜拜大神。。。

LL pow_mod(LL a, LL n)  
{  
    LL ans = 1;  
    while(n)  
    {  
        if(n & 1LL)  
            ans = ans * a % MOD;  
        a = a * a % MOD;  
        n >>= 1;  
    }  
    return ans;  
}  
LL l[MAXN], r[MAXN];  
LL cnt[MAXN];  
int rec[MAXN];  
int main()  
{  
    int m; Ri(m);  
    int top = 0;  
    for(int i = 1; i <= m; i++)  
    {  
        int a; Ri(a);  
        if(!cnt[a])  
            rec[++top] = a;  
        cnt[a]++;  
    }  
    l[0] = r[top+1] = 1;  
    for(int i = 1; i <= top; i++)  
        l[i] = l[i-1] * (cnt[rec[i]] + 1) % (MOD-1);  
    for(int i = top; i >= 1; i--)  
        r[i] = r[i+1] * (cnt[rec[i]] + 1) % (MOD-1);  
    LL ans = 1;  
    for(int i = 1; i <= top; i++)  
    {  
        LL Num = cnt[rec[i]];  
        if(Num)  
        {  
            LL num = l[i-1] * r[i+1] % (MOD-1);  
            num = (Num + 1) * Num / 2 % (MOD-1) * num % (MOD-1);  
            ans = ans * pow_mod(rec[i], num) % MOD;  
        }  
    }  
    Pl(ans);  
    return 0;  
}  

Codeforces E. Qwerty78 Trip

n*m的格子,其中有一个格子是坏的,问你从(1, 1) - (n, m)有多少种走法。

来自:https://blog.csdn.net/chenzhenyu123456/article/details/51545532

填满n * m的矩阵,发现这就是一个杨辉三角的组合数问题。 
在格子没有坏的情况下,(1, 1) - (n, m)的走法dp[1][1][n][m] = C(n+m-2, m-1)。 
当(x, y)坏的情况下,少出的情况就是dp[1][1][x][y] * dp[x][y][n][m]。 
我们把(x, y)看做(1, 1)然后求一下就好了。套个逆元。

typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 2 * 1e5 + 1;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
void add(LL &x, LL y) { x += y; x %= MOD; }
LL pow_mod(LL a, int n) {
    LL ans = 1;
    while(n) {
        if(n & 1) {
            ans = ans * a % MOD;
        }
        a = a * a % MOD;
        n >>= 1;
    }
    return ans;
}
LL f[MAXN];
LL C(int n, int m) {
    return f[n] * pow_mod(f[n-m], MOD-2) % MOD * pow_mod(f[m], MOD-2) % MOD;
}
int n, m;
int main()
{
    f[0] = 1LL;
    for(int i = 1; i <= 200000; i++) {
        f[i] = f[i-1] * i % MOD;
    }
    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        int x, y; scanf("%d%d", &x, &y);
        if(x == 1 && y == 1) {
            printf("0\n");
            continue;
        }
        printf("%lld\n", (C(n+m-2, m-1) - C(n+m-(x-1)-(y-1)-2, m-(y-1)-1) * C(x+y-2, y-1) % MOD + MOD) % MOD);
    }
    return 0;
}

CodeForces - 16C

题目大意:给你原始尺寸a:b 和 目标比例x:y , 问面积最大的长a和宽b。

来自:https://blog.csdn.net/qq_24477135/article/details/52789168

思路:首先要化简x:y,因为4:3肯定是8:6.(很重要)
其次,要开longlong。
1、如果化简后的比例比x或y大,显然不可以
2、然后就是讨论bx和ay谁比较大就可以了,前者大,按x分,否则按y分。

LL a , b , x , y;  
LL gcd(LL a , LL b)  
{  
    if(b == 0) return a;  
    else return gcd(b , a % b);  
}  
int main()  
{  
    while(scanf("%I64d %I64d %I64d %I64d" , &a , &b , &x , &y) != EOF)  
    {  
        LL g = gcd(x , y);  
        x /= g;  
        y /= g;  
        LL t1 = a / x;  
        LL t2 = b / y;  
        if(a < x || b < y)  
        {  
            printf("0 0\n");  
        }  
        else if(b * x > a * y)  
        {  
            printf("%I64d %I64d\n" , t1 * x , t1  * y);  
        }  
        else if(b * x < a * y)  
        {  
            printf("%I64d %I64d\n" , t2 * x , t2 * y);  
        }  
        else  
            printf("%I64d %I64d\n" , a , b);  
    }  
    return 0;  
}