万能欧几里得
记如果经过网格的一条横线,,就乘上矩阵,如果经过,乘上矩阵,如果同时经过则先再
例子:,我们维护向量
则经过时,,经过,
模板如下,复杂度为
cpp
template<typename T>
struct FloorSum{
T qpow(T a,int b){
T res;
while(b){
if(b&1)res=res*a;
a=a*a;
b>>=1;
}
return res;
}
T f(int a,int b,int c,int n,T U,T R){
if(!n)return T();
if(b>=c)return qpow(U,b/c)*f(a,b%c,c,n,U,R);
if(a>=c)return f(a%c,b,c,n,U,qpow(U,a/c)*R);
int m=((__int128)a*n+b)/c;
if(!m)return qpow(R,n);
return qpow(R,(c-b-1)/a)*U*f(c,(c-b-1)%a,a,m-1,R,U)*qpow(R,n-((__int128)c*m-b-1)/a);
}
};考虑模板题,要求的是,如何合并两个区间
定义表示左/右区间的的值(经过多少个)
总结:
- 左边求和的数量为,右边求和的数量为
- 合并时,右边的加上
- 初始化时,初始化,初始化以及其他和相关的答案
写出如下结构体
cpp
struct Info{
int x,y,sumx,sumy,sumxy,sumy2;
Info():x(0),y(0),sumx(0),sumy(0),sumxy(0),sumy2(0){}
Info operator * (const Info &r){
Info res;
res.x=(x+r.x)%mod;
res.y=(y+r.y)%mod;
res.sumx=(sumx+r.sumx+x*r.x%mod)%mod;
res.sumy=(sumy+r.sumy+y*r.x%mod)%mod;
res.sumy2=(sumy2+r.sumy2+2*y%mod*r.sumy%mod+r.x*y%mod*y%mod)%mod;
res.sumxy=(sumxy+r.sumxy+y*r.sumx%mod+x*r.sumy%mod+x*y%mod*r.x%mod)%mod;
return res;
}
};初始化,求解的是的范围,需要加上的答案
cpp
void solve(){
int n,a,b,c;
cin>>n>>a>>b>>c;
Info U,R;
U.y=R.x=R.sumx=1;
FloorSum<Info> f;
auto ans=f.f(a,b,c,n,U,R);
ans.sumy=(ans.sumy+b/c)%mod;
ans.sumy2=(ans.sumy2+(b/c)*(b/c))%mod;
cout<<ans.sumy<<" "<<ans.sumy2<<" "<<ans.sumxy<<"\n";
}求
维护
cpp
struct Matrix{
static constexpr int maxn=22;
int m[maxn][maxn];
Matrix(){
memset(m,0,sizeof m);
}
Matrix(int n){
init(n);
}
void init(int n){
memset(m,0,sizeof m);
for(int i=0;i<n;i++)m[i][i]=1;
}
Matrix operator*(const Matrix &a){
Matrix res;
for(int k=0;k<maxn;k++){
for(int i=0;i<maxn;i++){
for(int j=0;j<maxn;j++){
res.m[i][j]=(res.m[i][j]+m[i][k]*a.m[k][j]%mod)%mod;
}
}
}
return res;
}
Matrix operator+(const Matrix &a){
Matrix res;
for(int i=0;i<maxn;i++){
for(int j=0;j<maxn;j++){
res.m[i][j]=(m[i][j]+a.m[i][j])%mod;
}
}
return res;
}
};
struct Info{
Matrix sum,x,y;
Info():sum(),x(22),y(22){}
Info operator * (const Info &r){
Info res;
res.x=x*r.x;
res.y=y*r.y;
res.sum=sum+x*r.sum*y;
return res;
}
};
template<typename T>
struct FloorSum{
T qpow(T a,int b){
T res;
while(b){
if(b&1)res=res*a;
a=a*a;
b>>=1;
}
return res;
}
T f(int a,int b,int c,int n,T U,T R){
if(!n)return T();
if(b>=c)return qpow(U,b/c)*f(a,b%c,c,n,U,R);
if(a>=c)return f(a%c,b,c,n,U,qpow(U,a/c)*R);
int m=((__int128)a*n+b)/c;
if(!m)return qpow(R,n);
return qpow(R,(c-b-1)/a)*U*f(c,(c-b-1)%a,a,m-1,R,U)*qpow(R,n-((__int128)c*m-b-1)/a);
}
};
void solve(){
int p,q,r,l,n;
cin>>p>>q>>r>>l>>n;
Info R,U;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++)cin>>R.x.m[i][j],R.sum.m[i][j]=R.x.m[i][j];
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++)cin>>U.y.m[i][j];
}
FloorSum<Info> f;
auto ans=f.f(p,r,q,l,U,R);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++)cout<<ans.sum.m[i][j]<<" \n"[j==n-1];
}
}求(简单版)
cpp
int FloorSum(int a,int b,int c,int n){// \sum_{i=0}^{n} (ai+b)/c
if(!a)return b/c*(n+1);
if(a<0){
b+=n*a;
a=-a;
}
if(b<0){
int d=(a-b-1)/a;
n-=d;
b+=a*d;
if(n<0)return 0;
}
//======== 负数计入答案 ======================
// if(b<0){
// int k=(c-b-1)/c;
// return FloorSum(a,b+k*c,c,n)-k*(n+1);
// }
//===========================================
if(a>=c||b>=c)return n*(n+1)/2*(a/c)+(b/c)*(n+1)+FloorSum(a%c,b%c,c,n);
int m=(a*n+b)/c;
return m*n-FloorSum(c,c-b-1,a,m-1);
}求1~n除M模R的数的二进制1的数量
结论:对于一个数二进制第位为,有,另一种写法是
可以用类欧求解
求
因此启发我们维护
cpp
struct Info{
int x,y;
vector<vector<int>> sum;
Info():sum(vector (k1+1,vector<int>(k2+1))),x(0),y(0){}
Info operator * (const Info &r){
Info res;
res.x=(x+r.x)%mod;
res.y=(y+r.y)%mod;
int cx=x%mod,cy=y%mod;
for(int u=0;u<=k1;u++){
for(int v=0;v<=k2;v++){
res.sum[u][v]=sum[u][v];
for(int i=0,X=1;i<=u;i++,X=X*cx%mod){
for(int j=0,Y=1;j<=v;j++,Y=Y*cy%mod){
res.sum[u][v]=(res.sum[u][v]+comb.binom(u,i)*comb.binom(v,j)%mod*X%mod*Y%mod*r.sum[u-i][v-j]%mod)%mod;
}
}
}
}
return res;
}
};
void solve(){
int n,a,b,c;
cin>>n>>a>>b>>c>>k1>>k2;
FloorSum<Info> f;
Info U,R;
U.y=R.x=1;
for(int i=0;i<=k1;i++)R.sum[i][0]=1;
comb.init(15);
auto ans=f.f(a,b,c,n,U,R);
int res=ans.sum[k1][k2];
if(k1==0)res=(res+comb.qpow(b/c,k2))%mod;//k1=0,0^0=1,答案记得加上
cout<<res<<"\n";
}求的对数
cpp
void solve(){
int n,a,b;
cin>>n>>a>>b;
cout<<FloorSum(-a,n+b,b,n/a)<<'\n';
}求最简分数
cpp
// P5179
void work(int a,int b,int c,int d,int &p,int &q){
if(a<b&&c>d){
p=q=1;
return;
}
work(d%c,c,b-(d/c)*a,a,q,p);
q+=d/c*p;
}
void solve(){
int a,b,c,d;
while(cin>>a>>b>>c>>d){
int p,q;
work(a,b,c,d,p,q);
cout<<p<<"/"<<q<<"\n";
}
}求
按位考虑一个数第位为,等价于
定义表示
对于小于等于的数暴力做,这些块比较密集;大于的用整除分块+类欧
cpp
void solve(){
int n;
cin>>n;
int c=0;
for(int i=1;i<=min(n,(int)1e6);i++)c^=n%i;
vector<int> ans(50);
for(int l=1e6+1,r;l<=n;l=r+1){
int v=n/l;
r=n/(n/l);
for(int i=0;i<40;i++){
ans[i]+=FloorSum(-v,n-l*v,1ll<<i,r-l);
}
}
int res=0;
for(int i=0;i<39;i++){
res+=((ans[i]-2*ans[i+1])&1)*(1ll<<i);
}
cout<<(c^res)<<"\n";
}