A

假设 $a \geq b$

$$ \begin{aligned} \gcd(q^a-q,q^b-1) &= \gcd(q^b-1,q^a-q^b)\\ &= \gcd(q^b-1,q^b(q^{a-b}-1))\\ &= \gcd(q^b-1,q^{a-b}-1) \end{aligned} $$

使用取模加速运算即可。本质是对指数辗转相除。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

int q,a,b,ha;

inline int qpow(int a,int n=ha-2){
    int res = 1;
    while(n){
        if(n & 1) res = 1ll*res*a%ha;
        a = 1ll*a*a%ha;
        n >>= 1;
    }
    return res;
}

inline int gcd(int a,int b){
    if(a < b) return gcd(b,a);
    if(!b) return (qpow(q,a)+ha-1)%ha;
    return gcd(b,a%b);
}

int main(){
    int T;scanf("%d",&T);
    while(T--){
        scanf("%d%d%d%d",&q,&a,&b,&ha);
        LL x = qpow(q,a)-1,y = qpow(q,b)-1;
        printf("%d\n",gcd(a,b));
    }
    return 0;
}

B

开 1e6 个 vector 存一下所有的倍数,每次取出最少的,如果数量变成 $0$ 了就弹掉。

复杂度 $O(n \log n)$。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 1e6 + 5;

inline char nc(){
    #define SIZE 1000000+3
    static char buf[SIZE],*p1 = buf+SIZE,*p2 = buf+SIZE;
    if(p1 == p2){
        p1 = buf;p2 = buf+fread(buf,1,SIZE,stdin);
        if(p1 == p2) return -1;
    }
    return *p1++;
    #undef SIZE
}

template <typename T>
inline void read(T &x){
    x = 0;int flag = 0;char ch = nc();
    while(!isdigit(ch)){
        if(ch == '-') flag = 1;
        ch = nc();
    }
    while(isdigit(ch)){
        x = (x<<1) + (x<<3) + (ch^'0');
        ch = nc();
    }
    if(flag) x = -x;
}

bool p[MAXN];
int prime[MAXN],tot,d[MAXN];

inline void prework(){
    FOR(i,2,MAXN-1){
        if(!p[i]) prime[++tot] = i,d[i] = i;
        for(int j = 1;j <= tot && 1ll*i*prime[j] < MAXN;++j){
            p[i*prime[j]] = 1;d[i*prime[j]] = prime[j];
            if(!(i%prime[j])){
                d[i*prime[j]] = d[i];
                break;
            }
        }
    }
}

int a[MAXN],n,m,cnt[MAXN];
std::vector<int> dec[MAXN];
int pp[MAXN],ee[MAXN],sz;

inline void dfs(int dep,int id,int sm=1){
    if(dep == sz+1){
        dec[id].pb(sm);
        return;
    }
    dfs(dep+1,id,sm);
    FOR(i,1,ee[dep]) sm *= pp[dep],dfs(dep+1,id,sm);
}

inline void fj(int i){
    if(i == 1){
        dec[1].pb(1);
        return;
    }
    int x = i,las = d[x];x /= d[x];
    sz = 0;int cnt = 1;
    while(x != 1){
        if(las != d[x]){
            ++sz;
            pp[sz] = las;
            ee[sz] = cnt;
            las = d[x];cnt = 0;
        }
        x /= d[x];cnt++;
    }
    ++sz;pp[sz] = las;ee[sz] = cnt;
    dfs(1,i);
}

std::vector<int> G[MAXN];

int main(){
    prework();
    read(n);
    FOR(i,1,n) read(a[i]),++cnt[a[i]];
    ROF(i,MAXN-1,1){
        if(cnt[i]){
            fj(i);
            for(auto x:dec[i]) G[x].pb(i);
        }
    }
    read(m);std::vector<int> ans;
    FOR(i,1,m){
        int x;read(x);
        while(!G[x].empty() && !cnt[G[x].back()]) G[x].pop_back();
        if(G[x].empty()) break;
        int v = G[x].back();
        ans.pb(v);cnt[v]--;
    }
    printf("%d\n",(int)ans.size());
    for(auto x:ans) printf("%d ",x);puts("");
    return 0;
}

C

拆式子:

$$ \begin{aligned} &\sum_{i=1}^n \sum_{j=i+1}^n A_i^2B_j^2+A_j^2B_i^2-A_iA_jB_iB_j\\ &= (\sum_{i=1}^n A_i^2)(\sum_{j=1}^n B_j^2) - (\sum_{i=1}^n A_iB_i)^2\\ \end{aligned} $$

树状数组分开维护就好了。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 5e5 + 5;
const int ha = 998244353;
const int inv2 = 499122177;

inline void add(int &x,int y){
    x += y;if(x >= ha) x -= ha;
}

int a[MAXN],b[MAXN];

inline char nc(){
    #define SIZE 1000000+3
    static char buf[SIZE],*p1 = buf+SIZE,*p2 = buf+SIZE;
    if(p1 == p2){
        p1 = buf;p2 = buf+fread(buf,1,SIZE,stdin);
        if(p1 == p2) return -1;
    }
    return *p1++;
    #undef SIZE
}

template <typename T>
inline void read(T &x){
    x = 0;int flag = 0;char ch = nc();
    while(!isdigit(ch)){
        if(ch == '-') flag = 1;
        ch = nc();
    }
    while(isdigit(ch)){
        x = (x<<1) + (x<<3) + (ch^'0');
        ch = nc();
    }
    if(flag) x = -x;
}

struct BIT{
    #define lowbit(x) ((x)&(-(x)))
    int tree[MAXN];

    inline void add(int pos,int x){
        while(pos < MAXN){
            ::add(tree[pos],x);
            pos += lowbit(pos);
        }
    }

    inline int calc(int pos){
        if(!pos) return 0;
        int res = 0;
        while(pos){
            ::add(res,tree[pos]);
            pos -= lowbit(pos);
        }
        return res;
    }

    inline int query(int l,int r){
        return (calc(r)+ha-calc(l-1))%ha;
    }
}bit1,bit2,bit3;

int n,q;

int main(){
    read(n);read(q);
    FOR(i,1,n) read(a[i]),bit1.add(i,1ll*a[i]*a[i]%ha);
    FOR(i,1,n) read(b[i]),bit2.add(i,1ll*b[i]*b[i]%ha);
    FOR(i,1,n) bit3.add(i,1ll*a[i]*b[i]%ha);
    FOR(i,1,q){
        int opt;read(opt);
        if(opt == 1){
            int l,r;read(l);read(r);
            int a = bit1.query(l,r),b = bit2.query(l,r),c = bit3.query(l,r);
            int res = (1ll*a*b%ha+ha-1ll*c*c%ha)%ha;
            printf("%d\n",res);
        }
        else{
            int p;read(p);
            bit1.add(p,ha-1ll*a[p]*a[p]%ha);
            bit2.add(p,ha-1ll*b[p]*b[p]%ha);
            bit3.add(p,ha-1ll*a[p]*b[p]%ha);
            read(a[p]);read(b[p]);
            bit1.add(p,1ll*a[p]*a[p]%ha);
            bit2.add(p,1ll*b[p]*b[p]%ha);
            bit3.add(p,1ll*a[p]*b[p]%ha);
        }
    }
    return 0;
}

D

这个题循环是假的,实际是给定了一些不等式关系,要求你确定有多少组解。

我们只关心相对大小关系;如果相对大小关系不同方案绝对不同,相对大小关系也决定了有多少组数是相等的。

所以设 $f_{i,S}$ 表示从小到大选出了 $i$ 组,已经用了 $S$ 内的数的方案数,每次枚举一个子集,要满足 $S$ 内的数都不能大于这个子集内的数,转移即可。

然后就可以用组合数计算答案了。这里注意到 $m$ 很小,可以手动约分。(居然之前没注意过)

加强

复杂度可以做到 $O(n^2 \cdot 2^n)$。

做法是我们首先缩点,发现一个 SCC 内的点一定同时选,于是图变成了 DAG ,选择方案合法当且仅当当前选出的集合没有指向之前集合的边。。所以还要设 $f_{i,j,S}$ 表示考虑到第 $i$ 个点,用了 $j$ 个集合,选择了 $S$ 内的点,然后转移枚举这个是否要新建集合。。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 15 + 1;

int n,m,ha;

inline void add(int &x,int y){
    x += y;if(x >= ha) x -= ha;
}
// a < b : a -> b
int small[(1<<MAXN)+3],big[(1<<MAXN)+3];
int f[MAXN][(1<<MAXN)+3];
// 选出了 S 内 i 组,组内相同的方案数
// 每次从小到大划分组

int tmp[MAXN];

inline int C(int n,int m){
    if(n < m) return 0;
    FOR(i,1,m) tmp[i] = n-i+1;
    FOR(i,2,m){
        int x = i;
        FOR(j,1,m){
            int g = std::__gcd(tmp[j],x);
            x /= g;tmp[j] /= g;
        }
    }
    int ans = 1;
    FOR(i,1,m) ans = 1ll*ans*tmp[i]%ha;
    return ans;
}

int main(){
    scanf("%d%d%d",&m,&n,&ha);//ha = 998244353;
    FOR(i,0,m-1){
        int k;scanf("%d",&k);
        while(k--){
            int x;scanf("%d",&x);--x;
            small[1<<i] |= (1<<x);
            big[1<<x] |= (1<<i);
        }
        scanf("%d",&k);
        while(k--){
            int x;scanf("%d",&x);--x;
            small[1<<x] |= (1<<i);
            big[1<<i] |= (1<<x);
        }
    }
    FOR(S,0,(1<<m)-1){
        FOR(i,0,m-1){
            if((S>>i)&1){
                small[S] |= small[S^(1<<i)];
                big[S] |= big[S^(1<<i)];
            }
        }
    }
    f[0][0] = 1;
    FOR(i,0,m-1){
        FOR(S,0,(1<<m)-1){
            if(!f[i][S]) continue;
            int s = ((1<<m)-1)^S;
            for(int T = s;T;T = (T-1)&s){
                if(((big[T]&S) == 0)){
                    add(f[i+1][S^T],f[i][S]);
                }
            }
        }
    }
    int ans = 0;
    FOR(i,1,m) (ans += 1ll*C(n,i)*f[i][(1<<m)-1]%ha) %= ha;
    printf("%d\n",ans);
    return 0;
}
Last modification:October 8th, 2020 at 10:15 am
如果觉得我的文章对你有用,请随意赞赏