2025“钉耙编程”中国大学生算法设计暑期联赛(10)——Multiple and Factor · znzryb
## 思路讲解
简单来说,就是对
gcd递推方法。
```cpp
FOR(i,0,blocksz){
FOR(j,0,blocksz){
if(i==0 || j==0){
GCD[i][j]=i+j;
continue;
}
if(i>j){
GCD[i][j]=GCD[i-j][j];
}else{
GCD[i][j]=GCD[i][j-i];
}
}
}
```
这个块长也比较玄学,$siz=\frac{N}{\sqrt M}$)是过不了的,$siz=\frac{N}{2\sqrt M}$ 就可以过了,$siz=\frac{N}{4\sqrt M}$ 更快?其实我感觉要正确生成块长,主要还是要根据这个真正需要这个块长的东西决定的。
在这一堆操作中,真正需要用到块长会增加复杂度的操作,比用块长可以减少复杂度的多,因此,取较小的块长可以加快速度。
可以看到,负担操作执行的更多,因此可以减少块长。

这个也是一个小技巧。
```cpp
// 比如说d=2,其实我们就是想知道k有几个可以除以2的因数
// 求这个O(1)比较难,但可以求k先除以2,即k/2有几个因数
ismul[d]+=SZ(divv[k/d])*x;
```
## AC代码
[https://vjudge.net/solution/63198297](https://vjudge.net/solution/63198297)
::: 源代码
```cpp
// by Gospel_rock
#include <bits/stdc++.h>
#include <cmath>
#include <cstdio>
#define all(vec) vec.begin(),vec.end()
#define PB push_back
#define EB emplace_back
#define EM emplace
#define FI first
#define SE second
#define pct __builtin_popcountll
#define ctz __builtin_ctzll
#define SZ(a) ((long long) a.size())
#define FOR(i, a, b) for (long long i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (long long i = (a); i >= (b); --i)
#define debug(var) cerr << #var <<":"<<var<<"\n";
#define cend cerr<<"\n-----------\n"
#define fsp(x) fixed<<setprecision(x)
#define minn(vec) *min_element(vec.begin(),vec.end())
#define maxx(vec) *max_element(vec.begin(),vec.end())
using namespace std;
#ifdef LOCAL
template<typename T> void _print(T x) { cerr << x; }
// 可以打印多层数组,但是格式不是很好
template<typename T> void _print(vector<T> v) { cerr << "[ "; for (T i : v) _print(i), cerr << " "; cerr << "]"; }
template<typename T> void _print(set<T> s) { cerr << "{ "; for (T i : s) _print(i), cerr << " "; cerr << "}"; }
template<typename T, typename U> void _print(pair<T, U> p) { cerr << "{"; _print(p.first); cerr << ", "; _print(p.second); cerr << "}"; }
template<typename T, typename U> void _print(map<T, U> m) { cerr << "{ "; for (auto &p : m) _print(p), cerr << " "; cerr << "}"; }
// 打印两层数组,格式好一些
template<typename T> void _print(vector<vector<T>> v){cerr<<"{\n";for (auto i : v) _print(i), cerr << "\n";cerr<<" }";}
#endif
using ll = long long;using ull = unsigned long long;
using DB=double;using LD=long double;
using i128 = __int128;
using pdd = pair<DB,DB>;using plb = pair<ll,bool>;
using pll = pair<ll,ll>;using vll=vector<ll>;using vb=vector<bool>;
using tll = tuple<ll,ll,ll>;using vii=vector<int>;
using vpll = vector<pll>;using vtll = vector<tll>;
using vdb = vector<DB>;using vc=vector<char>;
using arr3 = array<ll,3> ;using arr2 = array<ll,2>;
using CD=complex<double>;
static constexpr ll MAXN=(ll)5e5+10,INF=(ll)1e17+3; // 1e17+3是素数 1e18+9是素数
static constexpr ll mod=(ll)1e9+7; // 998244353;
static constexpr double eps=1e-8;const double pi=acos(-1.0);
ll N,M,Q,X,T,lT;
// 来自wida github仓库 https://github.com/hh2048/XCPC/tree/main 加了一些新功能
//46 ms https://acm.hdu.edu.cn/contest/view-code?cid=1179&rid=14912 读入字符getc(a) 的AC记录
namespace QuickRead { // 快读
char buf[1 << 21], *p1 = buf, *p2 = buf;
inline int getc() {
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
}
template <typename T> void Cin(T &a) {
T ans = 0;
bool f = 0;
char c = getc();
for (; c < '0' || c > '9'; c = getc()) {
if (c == '-') f = 1;
}
for (; c >= '0' && c <= '9'; c = getc()) {
ans = ans * 10 + c - '0';
}
a = f ? -ans : ans;
}
void getc(char &a){
char c=getc();
while (isspace(c)) {
c=getc();
}
a=c;
}
template <typename T, typename... Args> void Cin(T &a, Args &...args) {
Cin(a), Cin(args...);
}
// ===== 输出 =====
static constexpr size_t OUT_CAP = 1 << 21; // 大概是2MB
char obuf[OUT_CAP];
size_t op = 0;
inline void flush() {
if (op) {
fwrite(obuf, 1, op, stdout);
op = 0;
}
}
inline void putc(char c) {
if (op == OUT_CAP) flush();
obuf[op++] = c;
}
template <typename T> void write(T x) { // 注意,这里输出不带换行
if (x < 0) putc('-'), x = -x;
if (x > 9) write(x / 10);
putc(x % 10 + '0');
}
struct Flusher { // 析构时自动刷新
~Flusher() { flush(); }
} flusher;
} // namespace QuickRead
using namespace QuickRead;
inline void __();
constexpr ll blocksz=300;
ll GCD[blocksz+5][blocksz+5];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
FOR(i,0,blocksz){
FOR(j,0,blocksz){
if(i==0 || j==0){
GCD[i][j]=i+j;
continue;
}
if(i>j){
GCD[i][j]=GCD[i-j][j];
}else{
GCD[i][j]=GCD[i][j-i];
}
}
}
// cin>>lT;
// while(lT--)
__();
return 0;
}
/*
*/
ll B=0;
vll divv[MAXN];
inline void __(){
Cin(N,M);
B=max(N/int(sqrt(M)),2ll);
B=min({B,blocksz,N});
// B=1000;
// lazy存的是以i为因数的数的总和(懒标记)
vll lazy(B+5);
vll ismul(B+5); // i's multiple
for(int i=1;i<=N;++i){
for(int j=i;j<=N;j+=i){
divv[j].EB(i);
}
}
vll A(N+5);
FOR(i,1,N){
Cin(A[i]);
for(auto &d:divv[i]){
if(d>B) break;
// 这里有问题, lazy中的必须均匀
// lazy[d]+=A[i];
ismul[d]+=A[i];
}
}
FOR(_,1,M){
ll op,k,x;Cin(op,k);
if(op==1){ // mul
Cin(x);
if(k>B){
for(int i=k;i<=N;i+=k){
A[i]+=x;
}
}else{
lazy[k]+=x;
}
// 肯定不能在lazy上面加,因为这个是不均匀的
for(int i=1;i<=B;++i){
ll lcm=(i*k)/GCD[i][k%i];
ismul[i]+=(N/lcm)*x;
}
}else if(op==2){ // div
Cin(x);
for(auto &d:divv[k]){
A[d]+=x;
}
for(auto &d:divv[k]){
if(d>B) break;
// 比如说d=2,其实我们就是想知道k有几个可以除以2的因数
// 求这个O(1)比较难,但可以求k先除以2,即k/2有几个因数
ismul[d]+=SZ(divv[k/d])*x;
}
}else if(op==3){ // mul
ll ans=0;
if(k>B){
for(int i=1;i<=B;++i){
ll lcm=(i*k)/GCD[i][k%i];
ans+=(N/lcm)*lazy[i];
}
for(int i=k;i<=N;i+=k){
ans+=A[i];
}
}else{
ans+=ismul[k];
// for(int i=1;i<=B;++i){
// ll lcm=i*k/__gcd<int>(i,k);
// ans+=(N/lcm)*lazy[i];
// }
}
write(ans);putc('\n');
}else{ // div
ll ans=0;
for(auto &d:divv[k]){
ans+=A[d];
if(d<=B){
ans+=SZ(divv[k/d])*lazy[d];
}
}
write(ans);putc('\n');
}
}
}
/*
AC
https://vjudge.net/solution/63198297
*/
```
:::
## 心路历程(WA,TLE,MLE……)