P2086 [NOI2012] 魔幻棋盘 题解
考虑一种很暴力的做法。下文中令
为方便叙述,不妨设
考虑
注意到
于是只需要维护
由于
设有 std::gcd,需要使用 C++17 及以上的标准编译)。
上面的做法看起来在
放代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
class segtree{
private:
int k,m;
vector<ll> s,g;
inline void pushup(int u){
s[u]=s[u<<1]+s[u<<1|1];
g[u]=gcd(g[u<<1],g[u<<1|1]);
}
public:
void init(vector<ll> a){
k=__lg(a.size())+(__builtin_popcount(a.size())>1);
s.resize((m=1<<k)<<1),g=s;
for(int i=0;i<a.size();i++)
s[i+m]=g[i+m]=a[i];
for(int i=m-1;i;i--)
pushup(i);
}
inline void update(int p,ll d){
s[p+=m]+=d,g[p]+=d;
for(int i=1;i<=k;i++)
pushup(p>>i);
} // 单点修改
inline ll query_sum(int l,int r){
ll c=0; l+=m,r+=m;
while(l<r){
if(l&1)c+=s[l++];
if(r&1)c+=s[--r];
l>>=1,r>>=1;
}
return c;
} // 区间求和
inline ll query_gcd(int l,int r){
ll w=0; l+=m,r+=m;
while(l<r){
if(l&1)w=gcd(w,g[l++]);
if(r&1)w=gcd(w,g[--r]);
l>>=1,r>>=1;
} // 区间求 gcd
return w;
}
}; // 非递归线段树
int main(){
ios::sync_with_stdio(false);
int n,m,x,y,q; cin>>n>>m>>x>>y>>q,x--,y--;
bool f=n>m; if(f)swap(n,m),swap(x,y); // 处理 n>m 的情况
vector a(n,vector<ll>(m));
if(f){
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
cin>>a[j][i];
}
else for(auto &i:a)for(auto &j:i)cin>>j;
vector<segtree> t(n);
for(int i=0;i<n;i++){
vector<ll> d(m);
adjacent_difference(a[i].begin(),a[i].end(),d.begin());
t[i].init(d);
} // 预处理差分序列
while(q--){
int op; cin>>op;
if(op){
int xa,ya,xb,yb; ll d; cin>>xa>>ya>>xb>>yb>>d,xa--,ya--,xb--,yb--;
if(f)swap(xa,ya),swap(xb,yb);
for(int i=xa;i<=xb;i++)
if(t[i].update(ya,d);yb+1<m)t[i].update(yb+1,-d);
} // 修改
else{
int xa,ya,xb,yb; ll c=0; cin>>xa>>ya>>xb>>yb;
if(f)swap(xa,ya),swap(xb,yb);
for(int i=x-xa;i<=x+xb;i++)
c=gcd(c,gcd(t[i].query_gcd(y-ya+1,y+yb+1),t[i].query_sum(0,y-ya+1)));
cout<<c<<'\n';
} // 查询
}
return 0;
}