类欧几里得求解问题有如下形式:
普通floor_sum
若或者,令
就转化成了的情况
考虑的情况:
令
复杂度为
从几何意义讲,表示的是下的整数点
类似地,
的情况:
的情况:
洛谷P5170 【模板】类欧几里得算法
cpp
struct FloorSum{
static constexpr int MOD=998244353,inv2=499122177,inv6=166374059;
struct Node{
int f=0,g=0,h=0;
};
int mo(int x){return x>=MOD?x-MOD:x;}
int sqr(int x){return 1ll*x*x%MOD;}
Node f(int a,int b,int c,int n){
Node res;
if(!a){
res.f=1ll*b/c*n%MOD;
res.g=1ll*n*(n-1)/2%MOD*(b/c)%MOD;
res.h=1ll*sqr(b/c)*n%MOD;
}
else if(a>=c||b>=c){
Node o=f(a%c,b%c,c,n);
res.f=(o.f+1ll*n*(n-1)/2%MOD*(a/c)+1ll*b/c*n)%MOD;
res.g=(o.g+1ll*a/c*n%MOD*(n-1)%MOD*(n*2-1)%MOD*inv6+1ll*n*(n-1)/2%MOD*(b/c))%MOD;
res.h=(o.h+2ll*(a/c)*o.g+2ll*(b/c)*o.f+1ll*n*(n-1)%MOD*(n*2-1)%MOD*inv6%MOD*sqr(a/c)+1ll*n*(n-1)%MOD*(a/c)%MOD*(b/c)+1ll*n*sqr(b/c))%MOD;
}
else{
int m=(1ll*a*(n-1)+b)/c;
Node o=f(c,c-b-1,a,m);
res.f=(1ll*(n-1)*m-o.f+MOD)%MOD;
res.g=((1ll*m*n%MOD*(n-1)-o.h-o.f)%MOD+MOD)*inv2%MOD;
res.h=(1ll*(n-1)*m%MOD*m-mo(o.g*2)-o.f+MOD+MOD)%MOD;
}
return res;
}
};
void solve(){
FloorSum f;
int a,b,c,n;
cin>>n>>a>>b>>c;
auto res=f.f(a,b,c,n+1);
cout<<res.f<<" "<<res.h<<" "<<res.g<<"\n";
}万能欧几里得算法
具体内容参考这篇博客:https://www.cnblogs.com/apjifengc/p/17492207.html
下面只讲做法推导 记如果经过网格的一条横线,,就乘上矩阵,如果经过,乘上矩阵,如果同时经过则先再
例子:,我们维护向量
则经过时,,经过,
模板如下,复杂度为
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';
}其他例题
CF1098E
给定长度为的序列,计算出每个放进数组,从大到小排序;再计算放进数组,然后求出数组的中位数,
思路: 首先计算数组,以为右端点,向左gcd最多迭代次,st表预处理区间gcd,然后二分跳次,处理出每个数字的出现次数,这部分为 接下来二分答案mid,然后是推式子
- 如果区间都是相同的数,计算出最多容纳的数字
mx=min(cnt[i],mid/nums[i]),然后就是等差数列求和ans+=mx*(2*cnt[i]-mx+1)/2; - 然后是有不同的数字的情况,假设当前区间为,那么记,记
- 如果那么个左端点和个右端点任选,答案为
- 否则答案为
然后就可以套板子了 可以双指针维护左右端点
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(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);
}
template<typename T,typename F>
struct SpraseTable{
int n;
vector<vector<T>> st;
F op;
void init(int n,const vector<T>& a){
this->n=n;
st.assign(n+1,vector<T>(__lg(n)+1));
for(int i=1;i<=n;i++)st[i][0]=a[i];
for(int j=1;1<<j<=n;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
st[i][j]=op(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
}
}
T query(int l,int r){
int k=__lg(r-l+1);
return op(st[l][k],st[r-(1<<k)+1][k]);
}
};
SpraseTable<int,decltype([](int a,int b){return __gcd(a,b);})> st;
void solve(){
int n;
cin>>n;
vector<int> a(n+1);
for(int i=1;i<=n;i++)cin>>a[i];
st.init(n,a);
map<int,int> mp;
for(int i=1;i<=n;i++){
int x=i;
while(x>=1){
int l=1,r=x;
int g=st.query(x,i);
while(l<r){
int mid=l+r>>1;
if(st.query(mid,i)==g)r=mid;
else l=mid+1;
}
mp[g]+=x-l+1;
x=l-1;
}
}
vector<int> nums,cnt;
for(auto &[x,y]:mp){
nums.push_back(x);
cnt.push_back(y);
}
int C=n*(n+1)/2;
C=C*(C+1)/2;
if(C&1)C=(C+1)/2;
else C=C/2;
auto check=[&](int mid){
int ans=0;
for(int i=0;i<nums.size();i++){
int mx=min(cnt[i],mid/nums[i]);
ans+=mx*(2*cnt[i]-mx+1)/2;
}
if(nums.size()<=1)return ans>=C;
auto get=[&](int l,int r,int sum){
if(r>=nums.size())return 0ll;
int a=nums[l],b=nums[r],sa=cnt[l],sb=cnt[r];
if(a*sa+sum+b*sb<=mid)return sa*sb;
return FloorSum(-a,mid-sum,b,sa)-max(0ll,(mid-sum)/b)-FloorSum(-a,mid-sum-b*sb,b,sa-1)+max(0ll,(mid-sum-b*sb)/b);
};
for(int i=0,j=1,k=0,sum=0,tot=0;i<nums.size();i++){
if(nums[i]>mid)break;
if(i>=j)j=i+1,sum=tot=0;
ans+=tot*cnt[i];
while(j<nums.size()&&sum+cnt[j]*nums[j]<=mid){
ans+=get(i,j,sum);
sum+=cnt[j]*nums[j];
tot+=cnt[j];
j++;
}
if(j<nums.size())ans+=get(i,j,sum);
if(i+1<j){
sum-=cnt[i+1]*nums[i+1];
tot-=cnt[i+1];
}
}
return ans>=C;
};
int l=1,r=1e15;
while(l<r){
int mid=l+r>>1;
if(check(mid))r=mid;
else l=mid+1;
}
cout<<l<<"\n";
}CF1182F
题意: 求内的最大值对应的最小整数,
思路:
- 接近更优,二分这个距离,即
- 结论:
- 因此只需求和判断是否存在即可
- 求出最小距离后,用exgcd还原求解
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);
}
int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return a;
}
int g=exgcd(b,a%b,y,x);
y-=a/b*x;
return g;
}
void solve(){
int a,b,p,q;
cin>>a>>b>>p>>q;
int P=p<<1,Q=q<<1;
auto check=[&](int mid){
int l=q-mid,r=q+mid;
return FloorSum(P,-l,Q,b)-FloorSum(P,-l,Q,a-1)-FloorSum(P,-r-1,Q,b)+FloorSum(P,-r-1,Q,a-1)>0;
};
int l=0,r=1e9;
while(l<r){
int mid=l+r>>1;
if(check(mid))r=mid;
else l=mid+1;
}
int x,y;
int g=exgcd(P,Q,x,y);
int ans=1e9;
int dx=Q/g,dy=P/g;
if((q-l)%g==0){
int val=(q-l)/g*x;
val+=(a-val)/dx*dx;
while(val>=a)val-=dx;
while(val<a)val+=dx;
ans=min(ans,val);
}
if((q+l)%g==0){
int val=(q+l)/g*x;
val+=(a-val)/dx*dx;
while(val>=a)val-=dx;
while(val<a)val+=dx;
ans=min(ans,val);
}
cout<<ans<<"\n";
}abc313G
abc402G
2020ICPC沈阳I

对于前半部分:
对于后半部分:
似乎要计算负数部分和特判HM
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);
}
void solve(){
int H,M,A;
cin>>H>>M>>A;
int ans=H*M;
ans-=M*(H-1)-FloorSum(H-1,A,M,M-1);
ans-=FloorSum(H-1,M-A-1,M,M-1);
cout<<min(H*M,ans)<<"\n";
}补一发易理解的正解,设时间为,时针角速度,分针角速度
因此,
答案就是
当时会有重合,此时相当于角度小于等于,则任意时刻都满足,答案为
否则来看一下式子,令
如果,因为是的完全剩余系,所以也是的完全剩余系,答案就是
否则,考虑,变成上面的情况,答案就是,相当于将切分成段,因此总答案为
考虑
,答案就是
cpp
void solve(){
int H,M,A;
cin>>H>>M>>A;
if(2*A==H*M){
cout<<H*M<<"\n";
return;
}
int G=__gcd(H-1,H*M);
cout<<G*(A/G+1+(H*M-1)/G-(H*M-A+G-1)/G+1)<<"\n";
}LOJ6344
按位考虑一个数第位为,等价于
定义表示
对于小于等于的数暴力做,这些块比较密集;大于的用整除分块+类欧
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);
}
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";
}