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

1001:^ & ^

对第i位,如果A和B的第i位都是1,那么C的这一位需要为1,否则取0.C必须为正数,所以C可以取0的时候,要看A和B最小的一个为1一个为0的位出现的位置last,让C为1<

#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
using namespace std;
int main()
{
     
    int T;cin>>T;
    ll a,b,c;
    while(T--){
     
        scanf("%lld%lld", &a, &b);
        c = 0;
        ll k = 1;
        int last = 0;
        for(int i = 32; i >= 0; --i){
     
            ll ba = (a&(k<<i)), bb = (b&(k<<i));
            if(ba && bb){
     
                c |= (k<<i);
            }
            else if(ba^bb) last = i;
        }
        if(c == 0) c = 1<<last;
        cout<<c<<endl;
    }
}

1002: array

做法1:
在[1,r]找没出现的,相当于在[r+1,end]找出现过的,在最后一个位置加一个n+1。用主席树找[r+1,end]出现的最小的大于等于k的数字。每个操作一可以看成在end位置添加一个a[pos]。

#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
using namespace std;
const int maxn = 2e5 + 50;
int sz[maxn*20], lc[maxn*20], rc[maxn*20];
int T[maxn];
int a[maxn];
int n, m;
int tot;
void build(int pre, int &cur, int l, int r, int pos){
     
    cur = ++tot;
    sz[cur] = sz[pre]+1;
    lc[cur] = lc[pre]; rc[cur] = rc[pre];
    if(l == r) return;
    if(pos <= mid)
        build(lc[pre], lc[cur], l, mid, pos);
    else
        build(rc[pre], rc[cur], mid+1, r, pos);
}
int qry(int pre, int cur, int l, int r, int k)
{
     
    if(sz[cur] - sz[pre] == 0) return -1;
    if(l == r){
     
        return l;
    }
    int res = -1;
    if(k <= mid)
        res = qry(lc[pre], lc[cur], l, mid, k);
    if(res == -1)
        res = qry(rc[pre], rc[cur], mid+1, r, k);
    return res;
}
int main()
{
     
	int ca;scanf("%d", &ca);
	while(ca--){
     
        tot = 0;
        scanf("%d%d", &n, &m);
        int ans = 0;
        for(int i = 1; i <= n; ++i){
     
            scanf("%d", &a[i]);
            build(T[i-1], T[i], 1, n+1, a[i]);
        }
        int p = n+1;
        build(T[n], T[p], 1, n+1, n+1);
        while(m--){
     
            int op; scanf("%d", &op);
            if(op == 1){
     
                int pos; scanf("%d", &pos);
                pos = pos^ans;
                if(a[pos] == -1) continue;
                build(T[p], T[p+1], 1, n+1, a[pos]);
                p++;
                a[pos] = -1;
            }
            else{
     
                int r, k;
                scanf("%d%d", &r, &k);
                r ^= ans; k ^= ans;
                ans = qry(T[r], T[p], 1, n+1, k);
                printf("%d\n", ans);
            }
        }
	}
}

做法2:
线段树维护每个值出现的下标,查询的时候相当于找[k,n]中第一个出现的下标值大于等于r的值。操作一相当于使得相应的值下标值变为inf。
做法3:
先预处理出没有进行操作1的答案,然后用set维护每次操作1删除的数字,查询的时候就是min(ans[r],*lower_bound(k))

1003:array

后缀自动机fail树上dfs序建可持久化线段树(据说是人尽皆知傻逼题,不会写的我瑟瑟发抖

1004: path

类似最短路的思路,一开始先把所有的边放进set,然后每次弹出一个最短的路径去更新,如果当前set的size比max(k)还要大,那么看更新的路径是否比set中最长的还要长,如果是,那么就停止更新,否则把那条最长的路径删了,把当前更新的路径加进去。

#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define P pair
using namespace std;
const int maxn = 5e4 + 50;
vector<P> g[maxn];
vector<int> Q;
vector<ll> ans;
int n, m, t;
int mx;
struct node{
     
    ll dis;
    int v, id;
    node(ll _dis = 0, int _v = 0, int _id = 0) : dis(_dis), v(_v), id(_id){
     }
    bool operator < (const node& a)const{
     
        if(dis != a.dis) return dis < a.dis;
        else return id < a.id;
    }
};
int tot = 0;
set<node> s;
void init(){
     
    scanf("%d%d%d", &n, &m, &t);
    s.clear();
    ans.clear();
    Q.clear();
    for(int i = 1; i <= n; ++i) g[i].clear();
    mx = 0; tot = 0;

    while(m--){
     
        int u, v; ll w;
        scanf("%d%d%lld", &u, &v, &w);
        g[u].push_back(P(w, v));
        s.insert(node(w, v, ++tot));
    }
    for(int i = 1; i <= n; ++i) sort(g[i].begin(), g[i].end());
    for(int i = 0; i < t; ++i){
     
        int k; scanf("%d", &k);
        Q.push_back(k);
        mx = max(mx, k);
    }
}
void sol()
{
     
    for(int i = 0; i < mx; ++i){
     
        node temp = *s.begin();
        s.erase(*s.begin());
        ans.push_back(temp.dis);
        if(i == mx-1) break;

        int u = temp.v;
        ll dis = temp.dis;
        for(int j = 0; j < g[u].size(); ++j){
     
            ll w = g[u][j].first;
            int v = g[u][j].second;
            if(i + s.size() > mx){
     
                set<node>::iterator it = --s.end();
                if(dis + w >= (*it).dis) break;
                s.erase(it);
                s.insert(node(dis+w, v, ++tot));
            }
            else{
     
                s.insert(node(dis+w, v, ++tot));
            }
        }
    }
    for(int i = 0; i < Q.size(); ++i){
     
        int k = Q[i];
        printf("%lld\n", ans[k-1]);
    }
}
int main()
{
     
	int T;cin>>T;
	while(T--){
     
        init();sol();
	}
}

1006:Shuffle Card

倒着写,最后一次出现的数字一定在第一个,然后依次放,如果出现过就不管他。处理完之后看按剩下的数字原本的顺序放。

1007:Windows Of CCPC

按题意模拟。

#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
using namespace std;
int a[1<<11][1<<11];
void sol(int k)
{
     
    int len = 1<<k;
    for(int i = 0; i < len; ++i){
     
        for(int j = 0; j < len; ++j){
     
            if(a[i][j] == 0) printf("C");
            else printf("P");
        }printf("\n");
    }
}
int main()
{
     
    a[0][0] = a[0][1] = 0;
    a[1][0] = 1; a[1][1] = 0;
    for(int i = 2; i <= 10; ++i){
     
        int len = (1<<(i-1));
        for(int x = 0; x < len; ++x){
     
            for(int y = 0; y < len; ++y){
     
                a[x][y+len] = a[x+len][y+len] = a[x][y];
                a[x+len][y] = a[x][y]^1;
            }
        }
    }
	int T;cin>>T;
	while(T--){
     
        int k;
        cin>>k;
        sol(k);
	}
}

1008:Fishing Master

抓第一条鱼的花费k和煮鱼的总时间是一定要花费的。然后你可以在煮鱼的时候去钓鱼,那么额外时间就出现在你去钓鱼的途中鱼已经煮好了,但是你得钓完鱼才能继续煮鱼。所以如果你可以在煮鱼的时间内把n-1条鱼都钓走,那么就可以没有额外花费,否则就按照煮鱼时间对k的模数从大到小选择额外间隔时间,额外时间是(k-mod),mod是t对k的模数。

#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int maxn = 1e5 + 50;
ll t[maxn];
ll m[maxn];
ll k;
int n;
int main()
{
     
	int T;cin>>T;
	while(T--){
     
        scanf("%d%lld", &n, &k);
        ll num = 0, ans = k;
        for(int i = 0; i < n; ++i) {
     
            scanf("%d", &t[i]);
            m[i] = t[i]%k;
            num += t[i]/k;
            ans += t[i];
        }
        if(num >= n-1){
     
            cout<<ans<<endl;
        }
        else{
     
            sort(m, m+n);
            for(int i = 0; i < n-1-num; ++i){
     
                ans += (k-m[n-1-i]);
            }
            cout<<ans<<endl;
        }
	}
}

你可能感兴趣的:(2019暑假补题)