1. 程式人生 > 實用技巧 >[HDU-6847] Decision (2020多校7T4)

[HDU-6847] Decision (2020多校7T4)

[HDU-6847] Decision (2020多校7T4)

列舉\(|v_1-v_2|\)後,可以遞推,用含首項(\(v_1+v_2\))的一次函式表示函式值為\(a(v_1+v_2)+b\),則問題等價於求

\(\begin{aligned} \sum_{i=0}^n [2|(ai+b)\mod m] \end{aligned}\) ,其中 \(n\) 對於每個 \(v_1-v_2\) 是不同的

這個問題,可以轉化為一個簡單的類歐幾里得問題

\((ai+b)\mod m \mod 2= ((ai+b)-(m\mod 2)\cdot \lfloor \frac{ai+b}{m}\rfloor )\mod 2\)

這個式子即把每次被\(m\)取模減少的\(m\)算進貢獻

可以看到操作非常簡單,可以直接套上萬能歐幾里得的板子

當然,也可以對於\(m\)的各種情況討論,轉化為求\(\lfloor \frac{ai+b}{m}\rfloor\),其主要思想還有應用 \(x\mod 2=x-2\cdot \lfloor \frac{x}{2}\rfloor\) 的轉化

我比賽時去抄了自己的類歐幾里得模板

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)

char IO;
template <class T=int> T rd(){
    T s=0; int f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=1e6+10;

int t,a,c,m;
int fx[N],fy[N];

ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b); }

ll D2(ll n){
    return n*(n+1)/2;
}
ll F(ll a,ll b,ll c,ll n){
    if(n<0) return 0;
    if(a==0) return (b/c)*(n+1);
    if(a>=c || b>=c) {
        ll ans=F(a%c,b%c,c,n)+D2(n)*(a/c)+(n+1)*(b/c);
        return ans;
    }
    ll t=(a*n+b)/c;
    ll ans=t*n-F(c,-b+c-1,a,t-1);
    return ans;
}

ll CalcMod(int a,int b,int m,int n){
    return F(a,b,m,n)-F(a,b,m*2,n)*2;
}

ll Calc(int a,int b,int n){
    // for i= [l,r] (ax+b) %m %2
    if(~m&1){
        // (ax+b)%2;
        a%=2,b%=2;
        if(a==0) return b*(n+1);
        return (n+1+b)/2;
    } else {
        // (ax+b) %m %2 = ((ax+b)/m + ax+b)%2
        int c=a%2,d=b%2;
        if(c==0) {
            // =((ax+b)/m+b)%2;
            if(d==0) return CalcMod(a,b,m,n);
            else return (n+1)-CalcMod(a,b,m,n);
        } else {
            // (ax+b) %m %2 = ((ax+b)/m + (x&1)+d)%2
            ll ans=0;

            // i%2 == 0
            ll t=CalcMod(a*2,b,m,n/2);
            if(d) t=n/2+1-t;
            ans+=t;

            b+=a;
            // i%2 == 1
            n--;
            if(n<0) return ans;
            t=CalcMod(a*2,b,m,n/2);
            if(!d) t=n/2+1-t;
            ans+=t;
            return ans;
        }
    }
}

int main(){
    rep(kase,1,rd()) {
        t=rd(),a=rd(),c=rd(),m=rd();
        fx[0]=1,fy[0]=0;
        rep(i,1,t) fx[i]=1ll*fx[i-1]*a%m,fy[i]=(1ll*fy[i-1]*a+c)%m;
        ll ans=0;
        rep(i,0,t) {
            if(i==0) {
                ans+=Calc(fx[i]*2%m,fy[i],t);
            } else {
                // a<b, b - a = i
                // a=[0,m-i]
                // b=[i,m]
                ll tmp=(1ll*i*fx[i]+fy[i])%m;
                ans+=Calc(fx[i]*2%m,tmp,t-i)*2;
            }
        }
        ll tmp=1ll*(t+1)*(t+1);
        ans=tmp-ans;
        ll g=gcd(tmp,ans);
        printf("%lld/%lld\n",ans/g,tmp/g);
    }
}