其他分享
首页 > 其他分享> > CCPC2019网络赛

CCPC2019网络赛

作者:互联网

2019中国大学生程序设计竞赛(CCPC) - 网络选拔赛

A

题意:找到最小的正整数 C 使得 (A^C)&(B^C) 最小。 \(A,B \le 10^9\)

签到题。这个C取 A&B 时为 0 ,并且此时也是最小的。注意要正整数,所以要跟 1 取 max。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int SZ = 1e6 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    if(flag) n = -n;
    return n;
}

LL work(LL n,LL m) {
    for(LL k = 0;;k ++) {
        if(((n^k)&(m^k)) == 0) return k;
    }
}

int main() {
    int T = read();
    while(T --) {
        LL n,m;
        scanf("%lld%lld",&n,&m);
        printf("%lld\n",max(1ll,n&m));
    }
}

B

题意:给一个排列,每次操作是给某个 \(a_x += 10^7\) ,或者询问最小的 v 使得其大于等于 \(k_i\) 且不等于任何一个 \(a_j\),\((1\le j\le r_i)\) 。 \(k_i \le n \le 10^5\)

key:线段树

对值域建线段树,每个点存这个数字的出现位置。每次修改操作实际上可以看做出现位置在无穷大处。每次即找一个最小的右端点 x,使得 [k,x] 的最大值大于 r。

由于线段树自带二分性,所以可以直接找,复杂度 \(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int SZ = 1e6 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    if(flag) n = -n;
    return n;
}

int a[SZ];

struct seg {
    int l,r,mx;
}tree[SZ * 4];

void update(int p) {
    tree[p].mx = max(tree[p<<1].mx,tree[p<<1|1].mx);
}

void build(int p,int l,int r) {
    tree[p].l = l;
    tree[p].r = r;
    if(l == r) {
        tree[p].mx = a[l];
        return ;
    }
    int mid = l + r >> 1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    update(p);
}

void change(int p,int x,int d) {
    if(tree[p].l == tree[p].r) {
        tree[p].mx = d;
        return ;
    }
    int mid = tree[p].l + tree[p].r >> 1;
    if(x <= mid) change(p<<1,x,d);
    else change(p<<1|1,x,d);
    update(p);
}

int ask_max(int p,int l,int r) {
    if(l <= tree[p].l && tree[p].r <= r) {
        return tree[p].mx;
    }
    int mid = tree[p].l + tree[p].r >> 1,ans = 0;
    if(l <= mid) ans = max(ans,ask_max(p<<1,l,r));
    if(mid < r) ans = max(ans,ask_max(p<<1|1,l,r));
    return ans;
}

int n,m;

int ask(int p,int l,int v) {
   // printf("%d [%d,%d] %d %d\n",p,tree[p].l,tree[p].r,l,v);
    if(tree[p].l == l) {
        if(tree[p].l == tree[p].r) {
            if(tree[p].mx <= v) return -1;
        //    cout << tree[p].l << endl;
            return tree[p].l;
        }
     //   printf("%d\n",tree[p<<1].mx);
        if(tree[p<<1].mx > v) return ask(p<<1,l,v);
        int mid = tree[p].l + tree[p].r >> 1;
        return ask(p<<1|1,mid+1,v);
    }
    int mid = tree[p].l + tree[p].r >> 1;
    if(mid < l) {
        return ask(p<<1|1,l,v);
    }
    else {
        int ans = ask(p<<1,l,v);
        if(ans != -1) return ans;
        return ask(p<<1|1,mid+1,v);
    }
}

int b[SZ];

int main() {
   // freopen("02.out","w",stdout);
    int T = read();
    while(T --) {
        n = read(),m = read();
        for(int i = 1;i <= n;i ++) a[b[i] = read()] = i;
        build(1,1,n);
        int lstans = 0;
        while(m --) {
            int o = read();
            if(o == 1) {
                int x = read() ^ lstans;
                if(b[x] == 1e9) continue;
                change(1,b[x],1e9);
                b[x] = 1e9;
            }
            else {
                int r = read() ^ lstans,k = read() ^ lstans;
                int ans = ask(1,k,r);
                if(ans == -1) ans = n+1;
                printf("%d\n",lstans = ans);
            }
        }
    }
}

C

题意:给一个字符串,每次询问一个区间的子串在整个字符串中出现第 k 次的位置。 \(n,Q \le 10^5\)

key:st表,后缀数组,主席树

想到后缀数组就差不多了,应该还有sam之类的做法,不过我不太会定位这个区间在sam中的位置……

后缀数组找到当前区间在lcp中的位置,向左右用rmq二分找到那个区间,这个字符串就在这个区间的每个位置上出现,找第 k 小就是静态区间 k 小,套个主席树。

找区间的时候有点小细节需要注意。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int SZ = 1e6 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    if(flag) n = -n;
    return n;
}

struct SuffixArray {
    /// 串从0开始,a[len]是非法字符
    /// sa[i]表示排名为i的后缀 i在[0,len-1]
    /// lcp[i]表示sa[i]和sa[i-1]的lcp i在[1,len-1]
    int lcp[SZ],sa[SZ],rk[SZ],len;
    bool cmp(int *y,int a,int b,int k) {
        int a1 = y[a],b1 = y[b];
        int a2 = a + k >= len ? -1 : y[a + k];
        int b2 = b + k >= len ? -1 : y[b + k];
        return a1 == b1 && a2 == b2;
    }

    int t1[SZ],t2[SZ],cc[SZ];

    void get_sa(char s[],int m) {
        int *x = t1,*y = t2; /// 字符集
        for(int i = 0;i < m;i ++) cc[i] = 0;
        for(int i = 0;i < len;i ++) ++ cc[x[i] = s[i]];
        for(int i = 1;i < m;i ++) cc[i] += cc[i - 1];
        for(int i = len - 1;~i;i --) sa[-- cc[x[i]]] = i;
        for(int k = 1;k < len;k <<= 1) {
            int p = 0;
            for(int i = len - k;i < len;i ++)  y[p ++] = i;
            for(int i = 0;i < len;i ++) if(sa[i] >= k) y[p ++] = sa[i] - k;
            for(int i = 0;i < m;i ++) cc[i] = 0;
            for(int i = 0;i < len;i ++) ++ cc[x[y[i]]];
            for(int i = 1;i < m;i ++) cc[i] += cc[i - 1];
            for(int i = len - 1;~i;i --) sa[-- cc[x[y[i]]]] = y[i];
            swap(x,y); m = 1; x[sa[0]] = 0;

            for(int i = 1;i < len;i ++)
                x[sa[i]] = cmp(y,sa[i - 1],sa[i],k) ? m - 1 : m ++;
            if(m >= len) break;
        }
    }

    void get_lcp(char s[]) {
        for(int i = 0;i < len;i ++) rk[sa[i]] = i;
        int h = 0;
        lcp[0] = 0;
        for(int i = 0;i < len;i ++) {
            if(!rk[i]) continue;
            int j = sa[rk[i] - 1];
            if(h) h --;
            while(s[i + h] == s[j + h]) h ++;
            lcp[rk[i]] = h;
        }
    }

    void init(char *s,int n,int m) {
        len = n;
        get_sa(s,m); get_lcp(s);
    }
}sa;

int st[SZ][22];

void get_st(int a[],int n) {
    for(int i = 1;i <= n;i ++) st[i][0] = a[i];

    for(int j = 1;j <= log2(n);j ++) {
        for(int i = 1;i + (1<<j) - 1 <= n;i ++) {
            st[i][j] = min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
        }
    }

  /*  for(int i = 1;i <= n;i ++) {
        for(int j = 0;j <= log2(n);j ++) {
            printf("%4d",st[i][j]);
        }
        puts("");
    }*/
}

int ask_min(int l,int r) {
    int k = log2(r-l+1);
    return min(st[l][k],st[r-(1<<k)+1][k]);
}

struct seg {
    int l,r,sz;
}tree[30000010];

int Tcnt = 0,rt[SZ];

void insert(int l,int r,int last,int &now,int v,int x) {
    now = ++ Tcnt;
    tree[now] = tree[last];
    tree[now].sz += x;
    if(l == r) return;
    int mid = (l + r) >> 1;
    if(v <= mid) insert(l,mid,tree[last].l,tree[now].l,v,x);
    else insert(mid + 1,r,tree[last].r,tree[now].r,v,x);
}

int ask_kth(int l,int r,int k) {
   // printf("[%d,%d] %d\n",l,r,k);
    if(r-l+1 < k) return -2;
    int L = 0,R = sa.len-1;
    int tl = rt[l-1],tr = rt[r];
    while(L != R) {
        int mid = L + R >> 1;
        int sz = tree[tree[tr].l].sz - tree[tree[tl].l].sz;
        if(sz >= k) {
            tl = tree[tl].l; tr = tree[tr].l; R = mid;
        }
        else {
            tl = tree[tl].r; tr = tree[tr].r; L = mid+1;
            k -= sz;
        }
    }
    return L;
}

pii ask(int p,int v) {
 //   cout << p << " " <<v << endl;
    pii ans;
    int L,R;
    if(sa.lcp[p] >= v) {
        L = 0,R = p;
        while(R - L > 1) {
            int mid = L + R >> 1;
         //   printf("[%d,%d] min: %d\n",mid,p,ask_min(mid,p));
            if(ask_min(mid,p) >= v) R = mid;
            else L = mid;
        }
        ans.first = R - 1;
    }
    else ans.first = p;

    L = p,R = sa.len;
    while(R - L > 1) {
        int mid = L + R >> 1;
        if(ask_min(p+1,mid) >= v) L = mid;
        else R = mid;
    }
    ans.second = L;
    return ans;
}

char s[SZ];

int main() {
    int T = read();
    while(T --) {
        int n = read(),m = read();
        scanf("%s",s);
        sa.len = strlen(s);
        sa.get_sa(s,256);
        sa.get_lcp(s);
/*
        for(int i = 0;i < sa.len;i ++) printf("%3d",i); puts("");
        for(int i = 0;i < sa.len;i ++) printf("%3d",sa.sa[i]); puts("");
        for(int i = 0;i < sa.len;i ++) printf("%3d",sa.lcp[i]); puts("");
        for(int i = 0;i < sa.len;i ++) printf("%3d",sa.rk[i]); puts("");
*/
        Tcnt = 0;
        for(int i = 1;i <= sa.len;i ++) insert(0,sa.len-1,rt[i-1],rt[i],sa.sa[i-1],1);

        get_st(sa.lcp,sa.len-1);

        while(m --) {
            int l = read(),r = read(),k = read();
            int ll = r - l + 1;
            l --;
            pii qj = ask(sa.rk[l],ll);
            printf("%d\n",ask_kth(qj.first+1,qj.second+1,k) + 1);
        }
    }
}
/**
233
5 5
aabaa
2 3 1

233
5 5
ababc
1 2 2
*/

D

题意:给一个带权有向图,每次询问第 k 短路径。 \(k,n,m,q \le 5*10^4\)

key:堆

一看到 k 很小,就想到每次从堆里拿出来一个拓展,多组询问离线就好了。

首先把边表按照权值排序,每个状态存 (当前点,当前边表中的第几条边,当前长度) ,每次取出一个,丢进去两个,分别是边表的下一个(如果有的话)和当前点走当前边表的这条边出去的第一条边。复杂度 \(O(k \log k)\)

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int SZ = 1e6 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    if(flag) n = -n;
    return n;
}

vector<pii> g[SZ];

struct haha {
    int u,id;
    LL w;
};

bool operator <(haha a,haha b) {
    return a.w > b.w;
}

struct hh {

    priority_queue<haha> q;

    void push(int u,int id,LL w) {
       // printf("%d %d %lld\n",u,id,w);
        q.push((haha){u,id,w});
    }

    haha top() {
        assert(q.size());
        return q.top();
    }

    void pop() {
        assert(q.size());
        haha f = q.top(); q.pop();
     //   printf("%d %d %lld:\n",f.u,f.id,f.w);
        int u = f.u,v = g[u][f.id].second;
        if(g[v].size()) {
            push(v,0,f.w+g[v][0].first);
        }

        f.w -= g[u][f.id].first;
        f.id ++;
        if(f.id < g[u].size())
            f.w += g[u][f.id].first,push(f.u,f.id,f.w);
      //  puts("----------");
    }

    void clr() {
        while(q.size()) q.pop();
    }
}q;


LL ans[SZ];
pii b[SZ];

int main() {
    int T = read();
    while(T --) {
        q.clr();
        int n = read(),m = read(),Q = read();
        for(int i = 1;i <= n;i ++) g[i].clear();
        for(int i = 1;i <= m;i ++) {
            int x = read(),y = read(),w = read();
            g[x].push_back(make_pair(w,y));
        }
        for(int i = 1;i <= n;i ++) sort(g[i].begin(),g[i].end());

        for(int i = 1;i <= n;i ++) {
            if(g[i].size())
                q.push(i,0,g[i][0].first);
        }
        for(int i = 1;i <= Q;i ++) {
            b[i].first = read();
            b[i].second = i;
        }
        sort(b+1,b+1+Q);
        int now = 1;
        for(int i = 1;i <= Q;i ++) {
            while(now < b[i].first) {
                q.pop();
                now ++;
            }
            //cout << now << endl;
            haha x = q.top();
            ans[b[i].second] = x.w;
        }
        for(int i = 1;i <= Q;i ++) printf("%lld\n",ans[i]);
    }
}

E

题意:给 n,a,b ,计算
\[ f(n,a,b)=\sum_{i=1}^n \sum_{j=1}^i gcd(i^a-j^a,i^b-j^b)[gcd(i,j)=1]\%(10^9+7) \]
其中 a,b 互质。 \(n,a,b \le 10^9\)

key:反演

打表可得,a,b 互质且 i,j 互质时那个 gcd 式子就是 i-j 。

然后就随便推推,要套个杜教筛

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int SZ = 5e6 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    if(flag) n = -n;
    return n;
}

LL ksm(LL a,LL b) {
    LL ans = 1;
    while(b) {
        if(b&1) ans = a * ans;
        a = a * a;
        b >>= 1;
    }
    return ans;
}

LL ksm(LL a,LL b,LL p) {
    LL ans = 1;
    while(b) {
        if(b&1) ans = a * ans % p;
        a = a * a % p;
        b >>= 1;
    }
    return ans;
}

const int MAXN = 5e6;
const int ni2 = (mod+1)/2;
const int ni6 = ksm(6,mod-2,mod);

bool vis[SZ];
int pri[SZ],tot,mu[SZ],smud[SZ];

void shai(int n) {
    mu[1] = 1;
    for(int i = 2;i <= n;i ++) {
        if(!vis[i]) pri[++ tot] = i,mu[i] = -1;
        for(int j = 1,m;j <= tot && (m=i*pri[j]) <= n;j ++) {
            vis[m] = 1;
            if(i%pri[j] == 0) {
                mu[m] = 0;
                break;
            }
            else {
                mu[m] = -mu[i];
            }
        }
    }
    for(int i = 1;i <= n;i ++) smud[i] = (smud[i-1] + i * mu[i]) % mod;
}

LL f1(LL n) {
    n %= mod;
    return n * (n + 1) % mod * ni2 % mod;
}

LL f2(LL n) {
    n %= mod;
    return n * (n + 1) % mod * (2*n+1) % mod * ni6 % mod;
}

LL f3(LL n) {
    return f1(n) * f1(n) % mod;;
}

unordered_map<int,int> smd;

int dfs(int n) {
    if(n <= MAXN) return smud[n];
    if(smd.count(n)) return smd[n];
    LL ans = 1;
    for(int i = 2,r;i <= n;i = r + 1) {
        r = n / (n / i);
        (ans -= dfs(n/i) * (f1(r) - f1(i-1)) % mod) %= mod;
    }
    smd[n] = ans;
    return ans;
}


LL baoli(int n,int a,int b) {
    LL ans = 0;
    for(int i = 1;i <= n;i ++) {
        for(int j = 1;j <= i;j ++) {
            if(__gcd(i,j) == 1) {
                LL t = __gcd(ksm(i,a)-ksm(j,a),ksm(i,b)-ksm(j,b));
             //   printf("%d %d %3lld\n",i,j,t);
                ans += t;
            }
        }
    }
    return ans;
}

LL S(LL n) {
    return ni2 * (f2(n) - f1(n)) % mod;
}

LL work1(int n) {
    LL ans = 0;
    for(int i = 1,r;i <= n;i = r + 1) {
        r = n / (n / i);
        (ans += (dfs(r) - dfs(i-1)) * S(n/i) % mod) %= mod;
    }
    ans += mod; ans %= mod;
    return ans;
}


int main() {
    shai(MAXN);
    int T = read();
    while(T --) {
        int n = read(),a = read(),b = read();
        printf("%lld\n",work1(n));
    }
}

K

题意:有一个三维无限大方格平面,每个格点上有权值 1 。修改 m 个形如 \(a_{n, x_i, y_i}\) 点的点权为 \(v_i\) 。之后每秒将改 \(a_{i,j,k}\) 将变为 \(a_{i+1,j+p,k} ^ {t1} \times a_{i+1,j,k+q} ^ {t2} \times a_{i+1,j,k} \times a_{i,j,k}\) 。求 \(n\) 秒之后 \(a_{0,0,0} \bmod 998244353\) 的值。 \(t1,t2,p,q,n \le 10^9,m \le 10^5\)

key:中国剩余定理,卢卡斯定理

考虑每个点的贡献。相当于有一个三元组 (x,y,z) ,每次 x 减 1,y 可以不变或者以 t1 的权值减 p,z 可以不变或者以 t2 的权值减 q,一条路径的权值是每步的权值之积。问从 \((x,y,z)\) 走到 \((0,0,0)\) 的所有路径权值之和,该值作为 \(v_i\) 的幂次乘给答案。

实际上一条路径的权值是一定的,所以问题在于求路径条数。这个是 \(\frac{n!}{a!b!c!}\) 的形式。即求 \(\frac{n!}{a!b!c!} \bmod (998244353=2^{23}\times 7 \times 17)\)

考虑分开,最后 crt 合并。7 和 17 的答案可以直接换成组合数用卢卡斯定理算,现在考虑阶乘模 \(2^{23}\) 怎么做。由于涉及到求逆,所以要把答案表示成 \(n!=A\times 2^B\) 的形式。

\(n!=1\times 2 \times 3 \times 4 \times 5 \times ... \times n = (1 \times 3 \times 5 \times ...) \times 2^{n/2} \times (1 \times 2 \times 3 \times ... \times n/2)\)

上面的除都是整除。

所以就预处理 \(1 \times 3 \times 5 \times ...\) ,这个显然关于 \(2^{22}\) 是循环节(因为 \(2^{23}+1 \mod 2^{23} = 1\)),所以就直接递归。

代码对于 7 和 17 没有用卢卡斯定理,也是用的同样的思路算的阶乘。不过这里要算一个形如 \(((p-1)! \bmod p)^m\) 的东西,根据威尔逊定理这东西只与 m 个奇偶性有关,所以可以 \(O(1)\) 计算,少个 log。

要 fread 读入优化,hdu卡常。把好多中间变量去掉了居然就过了,神奇……

#include<bits/stdc++.h>
using namespace std;

typedef unsigned int UI;
typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int SZ = 5e6 + 10;
const int INF = 1e9 + 10;
const int mod = 998244353;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a < '0' || a > '9') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') n = n * 10 + a - '0',a = getchar();
    if(flag) n = -n;
    return n;
}

int ksm(int a,int b,int p) {
    int ans = 1;
    while(b) {
        if(b&1) ans = 1ll * a * ans % p;
        a = 1ll * a * a % p;
        b >>= 1;
    }
    return ans;
}

int pri[] = {2,7,17};
int pk[] = {8388608,7,17};
int phi[] = {4194304,6,16};

int fac[8388608+10];
int fac7[110];
int fac17[110];

pii f[3][SZ];

const int B = 0;

void pre() {
    int p = 8388608 - 1;
    fac[0] = 1;
    fac[1] = 1;
    for(int i = 3;i <= 8388607;i += 2) {
        fac[i] = (1ll * fac[i-2] * i) & p;
        fac[i-1] = fac[i-2];
    }
    fac7[0] = 1;
    for(int i = 1;i < 7;i ++) fac7[i] = 1ll * fac7[i-1] * i % 7;
    fac17[0] = 1;
    for(int i = 1;i < 17;i ++) fac17[i] = 1ll * fac17[i-1] * i % 17;

    for(int i = 0;i < 3;i ++) {
        f[i][0].first = 1;
        for(int j = 1;j <= B;j ++) {
            int x = j,t = 0;
            while(x % pri[i] == 0) x /= pri[i],t ++;
            f[i][j].first = 1ll * f[i][j-1].first * x % pk[i];
            f[i][j].second = f[i][j-1].second + t;
        }
    }
}

pii dfs(int n,int p) {
    if(n <= B) {
        int id;
        if(p == 7) id = 1;
        else if(p == 17) id = 2;
        else id = 0;
        return f[id][n];
    }
    if(p == 7 || p == 17) {
        if(n < p) {
            return make_pair(p == 7 ? fac7[n] : fac17[n],0);
        }
        pii ans = dfs(n/p,p);
        (ans.first *= (n/p)&1 ? (p == 7 ? fac7[6] : fac17[16]) : 1) %= p;
        ans.first = ans.first * (p == 7 ? fac7[n%p] : fac17[n%p]) % p;
        ans.second += n / p;
        return ans;
    }
    else {
        if(n == 0) return make_pair(1,0);
        if(n == 1) return make_pair(1,0);
        //if(mp.count(make_pair(n,mod))) return mp[make_pair(n,mod)];
        pii ans = dfs(n >> 1,p);
        p --;
        ans.second += n >> 1;
        ans.first = (1ll * ans.first * fac[n & p]) & p;
        return ans;
    }
}

pii get_fac(int n,int p) {
    pii ans;
    if(p == 7 || p == 17) ans = dfs(n,p);
    else ans = dfs(n,8388608);
    /*int t = 1,mi = 0,mm = p == 2 ? 8388608 : p;
    for(int i = 1;i <= n;i ++) {
        int x = i;
        while(x%p==0) x/=p,mi++;
        t = 1ll * t * x % mm;
    }
    printf("%d! = %lld * %d^%lld\n",n,ans.first,p,ans.second);
    assert(t == ans.first); assert(mi == ans.second);*/
    return ans;
}

LL exgcd(LL a,LL b,LL &x,LL &y) {
    if(b == 0) {
        x = 1; y = 0;
        return a;
    }
    LL d = exgcd(b,a%b,x,y);
    LL t = x; x = y; y = t - a / b * y;
    return d;
}

LL excrt(LL *r,LL *a,int n){ // x%r=a
    LL M=a[1],R=r[1],x,y,d;
    for(int i=2;i<=n;i++){
        d=exgcd(M,a[i],x,y);
        x=(R-r[i])/d * x % a[i];
        R -= M*x;
        M = M/d * a[i];
    }
    return (R%M+M)%M;
}

int calc(int n,int a,int b,int c) {
    LL r[5],M[5];
    for(int i = 0;i < 3;i ++) {
        pii N = get_fac(n,pri[i]);
        pii A = get_fac(a,pri[i]);
        pii B = get_fac(b,pri[i]);
        pii C = get_fac(c,pri[i]);
        LL ans = 1ll * N.first
            * ksm(A.first,phi[i]-1,pk[i]) % pk[i]
            * ksm(B.first,phi[i]-1,pk[i]) % pk[i]
            * ksm(C.first,phi[i]-1,pk[i]) % pk[i];
        N.second -= A.second;
        N.second -= B.second;
        N.second -= C.second;
        ans = ans * ksm(pri[i],N.second,pk[i]) % pk[i];
        r[i+1] = ans;
        M[i+1] = pk[i];
    }
    return excrt(r,M,3);
}

struct FastIO{
  static const int S=1310720;
  int wpos,pos,len;char wbuf[S];
  FastIO():wpos(0){}
  inline int xchar(){
    static char buf[S];
    if(pos==len)pos=0,len=fread(buf,1,S,stdin);
    if(pos==len)return -1;
    return buf[pos++];
  }
  inline int xuint(){
    int c=xchar(),x=0;
    while(c<=32&&~c)c=xchar();
    if(c==-1)return -1;
    for(;'0'<=c&&c<='9';c=xchar())x=x*10+c-'0';
    return x;
  }
}io;

int main() {
  // freopen("1.in","r",stdin);
    //cout << (mod-1) / 2 / 7 * 6 / 17 * 16 << endl;
    pre();
/*
    int x;
    while(cin >> x) {
        for(int i = 0;i < 3;i ++) {
            get_fac(x,pri[i]);
        }
    }
*/
    int t1,t2,p,q,n,m;
    //while(~scanf("%d%d%d%d%d%d",&t1,&t2,&p,&q,&n,&m)) {
    while(1) {
        t1 = io.xuint();
        if(t1 == -1) break;
        t2 = io.xuint();
        p = io.xuint();
        q = io.xuint();
        n = io.xuint();
        m = io.xuint();
        LL ans = 1;
        for(int i = 1;i <= m;i ++) {
            //int x = read(),y = read(),v = read();
            //int x,y,v; scanf("%d%d%d",&x,&y,&v);
            int x = io.xuint(),y = io.xuint(),v = io.xuint();// scanf("%d%d%d",&x,&y,&v);
            if(x%p) continue;
            if(y%q) continue;
            int a = x / p,b = y / q,c = n - a - b;
            if(c<0) continue;
            int mi = calc(n,a,b,c);
            mi = 1ll * mi * ksm(t1,a,mod-1) % (mod-1) * ksm(t2,b,mod-1) % (mod-1);
        //    cout << mi << endl;
            ans = ans * ksm(v,mi,mod) % mod;
        }
        printf("%lld\n",ans);
    }
}

/**
1 1 1 1000000000 2 6
0 0 2
0 1 3
0 2 4
1 0 4
1 1 2
2 0 2

503044
*/

标签:CCPC2019,typedef,const,10,int,LL,网络,sa
来源: https://www.cnblogs.com/dqsssss/p/11403062.html