2019中国大学生程序设计竞赛(CCPC)

传送门

A.^&^

题意:
找到最小的正数(C),满足((A xor C)&(B xor C))最小。

思路:
输出(A&B)即可,特判答案为0的情况。

Code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int MAXN = 1e5+5,MAXM = 2e5+5,MOD = 1e9+7,INF = 0x3f3f3f3f,N = 1e6+1;
#define lson o<<1,l,m;
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
using namespace std;

int t;
ll A,B;
int main(){
    ios::sync_with_stdio(false);cin.tie(0);
    cin>>t;
    while(t--){
        cin>>A>>B;
        cout <<max(1LL,(A&B))<<'
';
    }
    return 0;
}

B.array

题意:
给出(a_1,cdots, a_n),满足两两各不相同且不超过(n),现有两种操作:

  • ((1,pos)),将(a_{pos})加上(inf)
  • ((2,r,k)),询问超过(k)的,且不等于(a_i,ileq r)的最小整数。

思路:
考虑权值线段树,那么对于(geq k)这个条件就很方便处理。
涉及下标,对于每个结点维护权值出现的位置(每个数出现一次),那么对于第二个限制,答案就为:位置大于(r)的最小的数。
对于修改操作,直接将位置设置为(inf),表示什么时候都可以用,因为无论这样,这个位置上面的权值都是可以使用的。
之后在线段树上面查询即可。
对于第一个条件会划分出(log)个区间,每个区间往下又是(log),所以会有两个(log)
但其实一个(log)即可,我们往下一次之后,就没必要找其它地方了,并且每次优先考虑往左子树走。

Code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int MAXN = 1e5+5,MAXM = 2e5+5,MOD = 1e9+7,INF = 0x3f3f3f3f,N = 1e6+1;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
using namespace std;

int t,n,m,a[MAXN],p[MAXN],maxv[MAXN<<2];
inline void pushUp(int o){
    maxv[o] = max(maxv[o<<1],maxv[o<<1|1]);
}
void build(int o,int l,int r){
    if(l==r){
        maxv[o] = p[l];
        return ;
    }
    int m = mid;
    build(lson);build(rson);
    pushUp(o);
}
void update(int o,int l,int r,int p){
    if(l==r){
        maxv[o] = INF;
        return ;
    }
    int m = mid;
    if(p<=m)update(lson,p);
    else update(rson,p);
    pushUp(o);
}
int query2(int o,int l,int r,int v){
    if(l==r){
        if(maxv[o]>v)return l;
        else return INF;
    }
    int m = mid;
    if(maxv[o<<1]>v)return query2(lson,v);
    else return query2(rson,v);
}
int query(int o,int l,int r,int v,int k){
    if(l>=k){
        return query2(o,l,r,v);
    }
    int m = mid;
    int ans = query(rson,v,k);
    if(k<=m){
        ans = min(ans,query(lson,v,k));
    }
    return ans;
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);
    cin>>t;
    while(t--){
        cin>>n>>m;
        for(int i=1;i<=n;i++)cin>>a[i],p[a[i]]=i;
        p[n+1] = INF;
        build(1,1,n+1);
        int op,r,k,lastans=0;
        while(m--){
            cin>>op;
            if(op==1){
                cin>>r;
                r^=lastans;
                r = a[r];
                update(1,1,n+1,r);
            }else{
                cin>>r>>k;
                r^=lastans;k^=lastans;
                //cout <<r<<' '<<k<<'
';
                lastans = query(1,1,n+1,r,k);
                cout <<lastans<<'
';
            }
        }
    }
    return 0;
}

C.K-th occurrence

题意:
给出一个字符串(S),回答多个询问:((l,r,k)),即(s_lcdots s_r)(k)次出现的位置。

思路:
对于处理多个相同子串的问题,可以考虑后缀数组。
然后这题挺考察对后缀数组的理解程度的。

  • 对于(s_l),通过(Rank[l])找到其在(sa)数组中的位置(pos);
  • 通过(pos)向前向后找尽可能长的区间,满足(lcpgeq r-l+1),那么就知道所有的子串个数且其后缀排名;
  • 但这样无法快速知道原串中第(k)出现的位置;
  • 问题等价于区间查询第(k)大;
  • 主席树!
  • 结点维护原位置信息就行,也就是(sa)数组。

稍微有点细节,详见代码:

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int T, n, q;
char s[N];
struct SA{                                       //sa:1...n  Rank:0...n-1
    int x[N], y[N], sa[N], c[N], height[N], Rank[N];
    int f[N][20], lg[N];
    int n;                                          //length
    void da(char *s, int m){
        n++;
        for(int i = 0; i < m; i++) c[i] = 0;
        for(int i = 0; i < n; i++) c[x[i] = s[i]]++;
        for(int i = 1; i < m; i++) c[i] += c[i - 1] ;
        for(int i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;
        for(int k = 1; k <= n; k <<= 1) {
            int p = 0 ;
            for(int i = n - k; i < n; i++) y[p++] = i ;
            for(int i = 0; i < n; i++) if(sa[i] >= k) y[p++] =sa[i] - k;
            for(int i = 0; i < m; i++) c[i] = 0;
            for(int i = 0; i < n; i++) c[x[y[i]]]++;
            for(int i = 1; i < m; i++) c[i] += c[i - 1];
            for(int i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i] ;
            swap(x , y); p = 1; x[sa[0]] = 0;
            for(int i = 1; i < n; i++)
                x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i-1] + k] == y[sa[i] + k] ? p - 1 : p++;
            if(p >= n) break ;
            m = p;
        }
        n--;
        int k = 0;
        for(int i = 0; i <= n; i++) Rank[sa[i]] = i;
        for(int i = 0; i < n; i++) {
            if(k) k--;
            int j = sa[Rank[i] - 1];
            while(s[i + k] == s[j + k]) k++;
            height[Rank[i]] = k;
        }
    }
    ll count() {
        ll ans = 0;
        for(int i = 1; i <= n; i++) ans += n - sa[i] - height[i];
        return ans;
    }
    void init() {
        for(int i = 2; i < N; i++) lg[i] = lg[i >> 1] + 1;
        for(int i = 2; i <= n; i++) f[i][0] = height[i];
        for(int j = 1; j < 20; j++)
            for(int i = 2; i + (1 << j) - 1 <= n; i++)
                f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]) ;
    }
    int get_lcp(int l, int r) {
        ++l; if(l > r) return n + 1;
        int k = lg[r - l + 1];
        return min(f[l][k], f[r - (1 << k) + 1][k]);
    }
}suf;
int rt[N * 22], ls[N * 22], rs[N * 22], sumv[N * 22];
int tot;
void build(int &o, int l, int r) {
    o = ++tot;
    ls[o] = rs[o] = sumv[o] = 0;
    if(l == r) return;
    int mid = (l + r) >> 1;
    build(ls[o], l, mid); build(rs[o], mid + 1, r);
}
void insert(int &o, int last, int l, int r, int v) {
    o = ++tot;
    sumv[o] = sumv[last] + 1;
    ls[o] = ls[last]; rs[o] = rs[last];
    if(l == r) return;
    int mid = (l + r) >> 1;
    if(v <= mid) insert(ls[o], ls[last], l, mid, v);
    else insert(rs[o], rs[last], mid + 1, r, v);
}
int query(int o, int last, int l, int r, int k) {
    if(l == r) return l;
    int mid = (l + r) >> 1;
    int sz = sumv[ls[o]] - sumv[ls[last]];
    if(sz >= k) return query(ls[o], ls[last], l, mid, k);
    else return query(rs[o], rs[last], mid + 1, r, k - sz);
}
int getl(int p, int sz) {
    int l = 1, r = p + 1, mid;
    while(l < r) {
        mid = (l + r) >> 1;
        if(suf.get_lcp(mid, p) >= sz) r = mid;
        else l = mid + 1;
    }
    return l;
}
int getr(int p, int sz) {
    int l = p, r = n + 1, mid;
    while(l < r) {
        mid = (l + r) >> 1;
        int tmp = suf.get_lcp(p, mid);
        if(suf.get_lcp(p, mid) >= sz) l = mid + 1;
        else r = mid;
    }
    return r - 1;
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> T;
    while(T--) {
        cin >> n >> q;
        cin >> s;
        suf.n = n;
        suf.da(s, 520);
        suf.init();
        tot = 0;
        build(rt[0], 0, n);
        for(int i = 1; i <= n; i++) {
            insert(rt[i], rt[i - 1], 0, n, suf.sa[i]);
        }
        while(q--) {
            int l, r, k; cin >> l >> r >> k;
            --l, --r;
            int pos = suf.Rank[l];
            int ll = getl(pos, r - l + 1);
            int rr = getr(pos, r - l + 1);
            if(rr - ll + 1 < k) {
                cout << -1 << '
';
            } else {
                cout << query(rt[rr], rt[ll - 1], 0, n, k) + 1 << '
';
            }
        }
    }
    return 0;
}

D.path

题意:
给出一个有向图,有(q)次询问,对于每次询问,输出其第(k)小路径。
(n,m,q,kleq 5*10^4)

思路:

  • 最直接的想法,直接贪心(bfs),加入所有边!
  • 很轻松就会被hack,比如菊花图。
  • 考虑减枝?但会影响正确性...似乎将上界设松一点可以过
  • 是否一次需要加入所有边?显然不是,但肯定要加入最小的边。
  • 考虑细化贪心,原来的贪心可能会加入很多没用的。
  • 对于条边,扩展两种状态:加上最小的一条边,或者换成次大的边。

这样就能枚举到所有的状态了。跟有一次牛客最小团的一个题比较类似。

Code
#include <bits/stdc++.h>
//#define heyuhhh ok
#define MP make_pair
#define sz(g) (int)g.size()
#define fi first
#define se second
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 5;
int T, n, m, Q;
vector <pii> g[N];
int query[N];
ll res[N];
struct node{
    int from, e;
    ll dis;
    bool operator < (const node &A) const {
        return dis > A.dis;
    }
};
int main() {
#ifdef heyuhhh
    freopen("input.in", "r", stdin);
#else
    ios::sync_with_stdio(false); cin.tie(0);
#endif
    cin >> T; ;
    while(T--) {
        cin >> n >> m >> Q;
        for(int i = 1; i <= n; i++) g[i].clear();
        for(int i = 1; i <= m; i++) {
            int u, v, w; cin >> u >> v >> w;
            g[u].push_back(MP(v, w));
        }
        for(int i = 1; i <= n; i++) {
            sort(g[i].begin(), g[i].end(), [&](pii A, pii B) {
                return A.se < B.se;
            });
        }
        int MAX = 0, cnt = 0;
        for(int i = 1; i <= Q; i++) cin >> query[i], MAX = max(MAX, query[i]);
        priority_queue <node> q;
        for(int i = 1; i <= n; i++) {
            if(sz(g[i])) q.push(node{i, 0, g[i][0].se});
        }
        while(!q.empty()) {
            node cur = q.top(); q.pop();
            res[++cnt] = cur.dis;
            if(cnt > MAX) break;
            int u = cur.from, e = cur.e;
            if(e + 1 < sz(g[u])) {
                q.push(node{u, e + 1, cur.dis - g[u][e].se + g[u][e + 1].se});
            }
            int v = g[u][e].fi;
            if(sz(g[v])) q.push(node{v, 0, cur.dis + g[v][0].se});
        }
        for(int i = 1; i <= Q; i++) {
            cout << res[query[i]] << '
';
        }
    }
    return 0;

E.huntian oy

题意:
定义(f(n,a,b)=sum_{i=1}^nsum_{j=1}^igcd(i^a-j^a,i^b-j^b)[gcd(i,j)=1]\% 10^9+7)
先有(T)组询问,每组询问给出(n,a,b),求(f(n,a,b))

思路:
先有一个公式如下:
(ageq b),且(a,b)互质,则有:

[egin{aligned} &gcd(a^n-b^n,a^m-b^m)\ =&gcd(a^m-b^m,a^{n\%m}-b^{n\%m})=a^{gcd(n,m)}-b^{gcd(n,m)} end{aligned} ]

我也不知道这咋证= =
反正有了这个式子之后就好推了:

[egin{aligned} f(n,a,b)=&sum_{i=1}^n sum_{j=1}^i i-j[gcd(i,j)=1]\ =&sum_{i=1}^n sum_{j=1}^i i[gcd(i,j)=1]-sum_{i=1}^n sum_{j=1}^i j[gcd(i,j)=1]\ =&sum_{i=1}^nivarphi(i)-sum_{i=1}^nfrac{ivarphi(i)+[i=1]}{2} end{aligned} ]

至于为什么后面那部分可以化成这样,这涉及到(gcd(n,m)=gcd(n-m,n)),也就是说,与之互素的数是成对出现的,并且其和为(n)。当然(n=1)的情况除外。
之后式子就可以化为这样:

[f(n,a,b)=frac{1}{2}sum_{i=1}^nivarphi(i)-1 ]

之后直接上杜教筛就是了。

Code
#include <bits/stdc++.h>
#define heyuhhh ok
using namespace std;
typedef long long ll;
const int N = 5e6 + 5, MOD = 1e9 + 7, inv6 = 166666668;
int p[N];
ll phi[N];
bool chk[N];
unordered_map <int, int> mp;
inline void init() {
    phi[1] = 1;
    int cnt = 0, k = N - 1, i;
    for(i = 2; i <= k; ++i) {
        if(!chk[i]) p[++cnt] = i, phi[i] = i - 1;
        for(int j = 1; j <= cnt && i * p[j] <= k; j++) {
            chk[i * p[j]] = 1;
            if(i % p[j] == 0) {phi[i * p[j]] = phi[i] * p[j]; break;}
            phi[i * p[j]] = phi[i] * (p[j] - 1);
        }
    }
    for(i = 1; i < N; ++i) {
        phi[i] = phi[i - 1] + 1ll * i * phi[i];
        if(phi[i] >= MOD) phi[i] %= MOD;
    }
}

int djs_phi(int n) {
    if(n <= 5000000) return phi[n];
    if(mp[n]) return mp[n];
    int ans = 1ll * (n + 1) * n % MOD * (2ll * n + 1) % MOD * inv6 % MOD;
    for(register int i = 2, j; i <= n; i = j + 1) {
        j = n / (n / i);
        ans -= ((ll(j - i + 1) * (j + i)) >> 1) % MOD * (ll)djs_phi(n / i) % MOD;
        ans %= MOD;
    }
    if(ans < 0) ans += MOD;
    return mp[n] = ans;
}
int n, a, b, T;
int main() {
#ifdef heyuhhh
    freopen("input.in", "r", stdin);
#else
    ios::sync_with_stdio(false); cin.tie(0);
#endif
    init();
    cin >> T;
    while(T--) {
        cin >> n >> a >> b;
        int ans = djs_phi(n) - 1;
        ans = (1ll * ans * (MOD + 1) / 2) % MOD;
        cout << ans << '
';
    }
    return 0;
}

## F.Shuffle Card 题意: 给出$n$张不同的牌,有$m$次操作,每次操作拿出一张牌放到最前面。 问最后牌的状态是什么。

思路:
考虑倒序操作即可。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int n, m;
int a[N], b[N], c[N];
bool vis[N];
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n >> m;
    vector <int> ans;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= m; i++) cin >> b[i];
    int now = 0;
    for(int i = m; i >= 1; i--) {
        if(!vis[b[i]]) {
            vis[b[i]] = 1;
            c[++now] = b[i];
        }
    }
    for(int i = 1; i <= n; i++) if(!vis[a[i]]) c[++now] = a[i];
    for(int i = 1; i <= now; i++) cout << c[i] << ' ';
    return 0;
}

G.Windows Of CCPC

没什么好说的,递归处理即可。

Code
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<cassert>
#define REP(r,x,y) for(register int r=(x); r<(y); r++)
#define REPE(r,x,y) for(register int r=(x); r<=(y); r++)
#ifdef sahdsg
#define DBG(...) printf(__VA_ARGS__)
#else
#define DBG(...) void(0)
#endif
using namespace std;

int _s; char _c;
template <class T>
void read(T&x) {
    x=0; do _c=getchar(); while(!isdigit(_c) && _c!='-'); _s=1; if(_c=='-') _s=-1, _c=getchar(); while(isdigit(_c)) { x=x*10+_c-'0'; _c=getchar();} x*=_s;
}

typedef long long LL;
#define MAXN 1000007
char ans[15][1028][1028];
int main() {
    ans[0][0][0]='C', ans[0][0][1]='C', ans[0][1][0]='P', ans[0][1][1]='C';
    REP(r,0,10)
    REP(i,0,1<<(r+1)) {
        REP(j,0,1<<(r+1)) {
            if(ans[r][i][j]=='C') {
                ans[r+1][i*2][j*2]='C', ans[r+1][i*2][j*2+1]='C';
                ans[r+1][i*2+1][j*2]='P', ans[r+1][i*2+1][j*2+1]='C';
            } else {
                ans[r+1][i*2][j*2]='P', ans[r+1][i*2][j*2+1]='P';
                ans[r+1][i*2+1][j*2]='C', ans[r+1][i*2+1][j*2+1]='P';
            }
        }
    }
    int T; read(T);
    while(T--) {
        int k; read(k); k--;
        DBG("!%d
", k);
        REP(i,0,1<<(k+1))    {
               REP(j,0,1<<(k+1)){
                //assert(ans[k][i][j]>' ');
                putchar(ans[k][i][j]);
            }
            putchar('
');
        }
    }
    return 0;
}

H.Fishing Master

贪心处理,队友写的,没有看。
反正能不浪费时间就不浪费时间。

Code
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<queue>
#define REP(r,x,y) for(register int r=(x); r<(y); r++)
#define REPE(r,x,y) for(register int r=(x); r<=(y); r++)
#ifdef sahdsg
#define DBG(...) printf(__VA_ARGS__)
#else
#define DBG(...) void(0)
#endif
using namespace std;

int _s; char _c;
template <class T>
void read(T&x) {
    x=0; do _c=getchar(); while(!isdigit(_c) && _c!='-'); _s=1; if(_c=='-') _s=-1, _c=getchar(); while(isdigit(_c)) { x=x*10+_c-'0'; _c=getchar();} x*=_s;
}

typedef long long LL;
#define MAXN 100007
struct node {
    int v, r;
    bool operator<(const node &rt) const {
        return v<rt.v;
    }
};
priority_queue<node> q;
int t[MAXN];
int main() {
    int T; read(T);
    while(0<T--) {
        while(!q.empty()) q.pop();
        int n,k; read(n); read(k);
        LL ans=(LL)k*n;
        REP(i,0,n) {
            read(t[i]);
        }
        REP(i,0,n) {
            ans+=t[i];
            q.push((node){min(k,t[i]),t[i]-k});
        }
        REP(i,1,n) {
            node now=q.top(); q.pop();
            ans-=now.v;
            if(now.r>0)
                q.push((node){min(k,now.r),now.r-k});
        }
        printf("%lld
", ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/heyuhhh/p/11404966.html