2023.11.3 做题记录

AcWing.97 约数之和

题目传送门

约数个数定理

对一个大于1的整数$n$,$n$可以分解质因数为$\prod_{i=1}^{k}p_i^{a_i} = {p_1}^{a_1}·{p_2}^{a_2}···{p_k}^{a_k}$

则n的正约数的个数就是$f(n) = \prod_{i=1}^{k}a_i+1=(a_1+1)(a_2+1)···(a_k+1)$,这个很好证明,因为$p_1^{a_1}$的约数有$p_1^0,p_1^1,p_1^2…p_1^{a1}$,共$(a_1+1)$个,同理$p_k^{a_k}$的约数有$(a_k+1)$个

约数定理

$n$的$(a_1+1)(a_2+1)···(a_k+1)$个正约数的和为:
$$
(p_1^0+p_1^1+…+p_1^{a_1})(p_2^0+p_2^1+…p_2^{a_2})…(p_k^0+p_k^1+…p_k^{a_k})
$$

对于这题来说,根据约数定理就有$A^B$的约数和为:
$$
(1+p_1^1+…+p_1^{Ba_1})(1+p_2^1+…p_2^{Ba_2})…(1+p_k^1+…p_k^{Ba_k})
$$

定义calc(n,k)为$p^0+p^1+…+p^k$,当 $k$ 为偶数,分成两部分的和变为$(p^0+…+p^{\frac{k}{2}})+(p^{\frac{k}{2}+1}+…p^k)$

后面的多项式提取$p^{\frac{k}{2}+1}$,变成$(p^0+…+p^{\frac{k}{2}})+p^{\frac{k}{2}+1} * (p^0+…p^{\frac{k}{2}})$

将两项合并$(1+p^{\frac{k}{2}+1}) * (p^0+…+p^{\frac{k}{2}})$,这个式子可以转化为$(1+p^{\frac{k}{2}}) * calc(p, \frac{k}{2})$

当 $k$ 为奇数时。即 $calc(p, k - 1)+p^{k-1}$

#include<bits/stdc++.h>

#define rint register int
#define int long long
#define endl '\n'

using namespace std;

const int mod = 9901;
int a, b;
int ans = 1;

//保存质因子以及出现的次数
unordered_map<int, int> primes;

void divide(int n) 
{
    for(int i = 2; i <= n / i; i++) 
	{
        if(n % i == 0) 
		{
            while(n % i == 0) 
			{
                primes[i]++;
                n /= i;
            }
        }
    }
    if(n > 1) 
	{
        primes[n]++;
    }
}

int qpow(int a, int b)
{
	int res = 1;
	while(b)
	{
		if (b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}

int calc(int p, int k) 
{
    if(k == 1) return 1;  
    if(k % 2 == 0) 
	{  
        return (qpow(p, k / 2) + 1) * calc(p, k / 2) % mod;
    }
    return (qpow(p, k - 1) + calc(p, k - 1)) % mod;
}

signed main()
{
    cin >> a >> b;

    divide(a);

    for(auto i : primes) 
	{
        int p = i.first;
		int k = i.second * b;
        ans = ans * calc(p, k + 1) % mod;
    }
    
    if (a == 0)
    {
        cout << 0 << endl;
        return 0;
    }
    
    cout << ans << endl;

    return 0;
}

[HNOI2003] 激光炸弹

题目传送门

二维前缀和即可:

\(f[i][j]=f[i−1][j]+f[i][j−1]−f[i−1][j−1]+a[i][j]\)

细节见代码:

#include <bits/stdc++.h>

#define rint register int
#define endl '\n'

using namespace std;

const int N = 5e3 + 5;

int s[N][N];
int n, m;
int max_x, max_y;
int ans;

signed main()
{
    cin >> n >> m;
    
    max_x = max_y = m;
    
    for (rint i = 1; i <= n; i++)
    {
        int x, y, z;
        cin >> x >> y >> z;
        x++, y++;
        s[x][y] += z;
        //如果不是 += z,可能出现重标记情况
        //洛谷数据太水,s[x][y] = z 也能过
        max_x = max(max_x, x);
        max_y = max(max_y, y);
        //初始化
        //二维前缀和不能便输入边存答案
    }
    
    for (rint i = 1; i <= max_x; i++)
    {
        for (rint j = 1; j <= max_y; j++)
        {
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + s[i][j];			
		}		
	}
            
    for (rint i = m; i <= max_x; i++)
    {
        for (rint j = m; j <= max_y; j++)
        {
            ans = max(ans, s[i][j] - s[i][j - m] - s[i - m][j] + s[i - m][j - m]);				
		}
	}
 
    cout << ans << endl;
    
    return 0;
}

Acwing.128 文本编译器

题目传送门
开两个栈即可:

    1. I x:把 \(x\) 插入栈 \(A\),更新栈 \(A\) 的栈顶位置的前缀和,更新栈 \(A\) 的栈顶位置的最大前缀和
    1. D:弹出栈 \(A\) 的栈顶元素
    1. L:弹出栈 \(A\) 的栈顶元素并插入栈 \(B\)
    1. R:弹出栈 \(B\) 的栈顶元素并插入栈 \(A\) 中,更新栈 \(A\) 的栈顶位置的前缀和,更新栈 \(A\) 的栈顶位置的最大前缀和
    1. Q k:直接返回 f[k]

SP4354 雪花

题目传送门

暴力 hash 即可

但是从每一个角都存一遍显然会寄,所以考虑对整个序列求和求积,然后对一个大质数取模得到 hash 值,存储上可以采用邻接表。

posted @ 2023-11-03 19:28  PassName  阅读(8)  评论(0编辑  收藏  举报