2018 China Collegiate Programming Contest Final (CCPC-Final 2018) 部分题解及AC代码

2018 China Collegiate Programming Contest Final (CCPC-Final 2018)

A - Mischievous Problem Setter

题意:一些题目的有难度值d,和需要花费的时间t,只能按难度递增的顺序解决问题,问在T时间内最多解决几题

题解:贪心

按难度值排序,然后能做就做,这题的意思是不能跳着做

#include 

using namespace std;

const int maxn = 1e5 + 5;
typedef long long ll;
int T;
struct node{
    int d, t;
}a[maxn];

bool cmp(node A, node B){
    if (A.d == B.d) return A.t < B.t;
    return A.d < B.d;
}
int n , m;
int main()
{
    scanf("%d", &T);
    for (int t = 0; t < T; t++){
        scanf("%d%d", &n, &m);
        for (int i = 0; i < n; i++)
            scanf("%d", &a[i].d);
        for (int i = 0; i < n; i++)
            scanf("%d", &a[i].t);
        sort(a, a + n, cmp);
        ll sum = 0, ans = -1;
        for (int i = 0; i < n; i++){
            if (sum + a[i].t <= m){
                sum += a[i].t;
            }else {
                ans = i;
                break;
            }
        }
        if (ans == -1) ans = n;
        printf("Case %d: %lld\n", t+1, ans);
    }

    return 0;
}

B - Balance of the Force

 题意:有一些骑士,每个骑士可以加入两个阵营,光明阵营(Light)和黑暗阵营(Dark),加入光明阵营的攻击力是L[i], 加入黑暗阵营的攻击力是D[i], 有一些骑士有矛盾,所以不能在同一个阵营,要是攻击力最大的骑士与攻击力最小的骑士的差最小,问骑士们如何选择阵营

题解:先dfs预处理一下,若是如果有一个骑士dfn存在二义性就不可能有解(比如有奇数个点的环),然后将这个连通图分为两部分,互相有矛盾的两个集合,每个连通图有两个状态(两个集合互相交换阵营),每个集合只保留最大值和最小值(保留其他值没有意义),然后按每个集合的最大值排序,枚举每个最大值,要是最小值最大,这样最大值和最小值的差就会最小。用线段树维护当前的最小值,当每一个集合至少有一个状态被维护即可计算最大值与最小值的差值,当这个集合的两个状态都被维护过以后,将这两个状态的最小值都设置成这两个最小值中较大的一个。

#include 
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)

#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long ll;
typedef pair pii;
const int maxn = 2e5+5;
const int INF = 0x3f3f3f3f;
vector G[maxn];
int color[maxn], n, m, val[maxn][2], sum[maxn];
int _mn[maxn<<3], pos[maxn];
bool vis[maxn];

struct node {
	int mx,mn;
	int id;
	bool operator < (const node& rhs) const {
		return mx < rhs.mx;
	}
}a[maxn*2];

void pushup(int rt) {
	_mn[rt] = min(_mn[rt<<1], _mn[rt<<1|1]);
}
void build(int l, int r, int rt) {
	_mn[rt] = INF;
	if(l == r) {
		return;
	}
	int mid = (l+r) >> 1;
	build(lson);
	build(rson);
	pushup(rt);
}
void update(int L, int v, int l, int r, int rt) {
	if(l == r) {
		_mn[rt] = v;
		return;
	}
	int mid = (l+r) >> 1;
	if(mid >= L)
		update(L,v,lson);
	if(mid < L)
		update(L,v,rson);
	pushup(rt);
}
int query(int L, int R, int l, int r, int rt) {
	if(l <= L && R <= r) {
		return _mn[rt];
	} else
		return INF;
	int mid = (l+r) >> 1;
	int res = min(query(L,R,lson),query(L,R,rson));
	return res;
}
void init() {
	for(int i = 0; i <= n; ++i) {
		G[i].clear();
		pos[i] = vis[i] = color[i] = 0;
	}
}
bool flag;
void dfs(int u, node& c1, node& c2) {
	if(!flag) return;
	c1.mx = max(c1.mx, val[u][0]);
	c1.mn = min(c1.mn, val[u][0]);
	c2.mx = max(c2.mx, val[u][1]);
	c2.mn = min(c2.mn, val[u][1]);
	for(auto v: G[u]) {
		if(color[v] == 0) {
			color[v] = 3-color[u];
			dfs(v,c2,c1);
		} else if(color[v] == color[u]) {
			flag = false;
			return;
		}
	}
}
int main() {
	int T;
	scanf("%d", &T);
	int ca = 0;
	while(T--) {
		printf("Case %d: ",++ca);
		init();
		scanf("%d%d", &n, &m);
		int u,v;
		for(int i = 0; i < m; ++i) {
			scanf("%d%d", &u, &v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		for(int i = 1; i <= n; ++i)
			scanf("%d%d", &val[i][0], &val[i][1]);
		int sz = 1;
		flag = true;
		int cnt = 0;
		for(int i = 1; i <= n; ++i) {
			if(!color[i]) {
				cnt++;
				a[sz].mx = -INF; a[sz].mn = INF; a[sz].id = i;
				a[sz+1] = a[sz];
				color[i] = 1;
				dfs(i, a[sz], a[sz+1]);
				sz += 2;
			}
		}
		if(!flag) {
			puts("IMPOSSIBLE");
			continue;
		}
		sort(a+1,a+sz);

		// for(int i = 1; i < sz; ++i) {
		// 	cout << a[i].id <<" " << a[i].mx <<" " << a[i].mn << endl;
		// }
		for(int i = 1; i < sz; ++i) {
			sum[i] = sum[i-1];
			if(!vis[a[i].id]) {
				sum[i]++;
				vis[a[i].id]= 1;
			}
		}
		build(1,sz-1,1);
		int ans = INF;
		for(int i = 1; i < sz; ++i) {
			int curmx = a[i].mx, curmn = a[i].mn;
			if(sum[i] == cnt) {
				if(pos[a[i].id]) {
					update(pos[a[i].id], INF, 1,sz-1,1);
				}

				curmn = min(curmn, query(1,i-1, 1,sz-1,1));
				ans = min(ans, curmx-curmn);
			}
			if(pos[a[i].id]) {
				int ml = max(a[i].mn, a[pos[a[i].id]].mn);
				update(pos[a[i].id], ml, 1,sz-1,1);
				update(i, ml, 1,sz-1,1);
			} else {
				update(i,a[i].mn,1,sz-1,1);
				pos[a[i].id] = i;
			}
		}
		printf("%d\n", ans);

	}
	return 0;
}

G - Pastoral Life in Stardew Valley

题意:给一块田野,田野中放置稻草人,稻草人要被稻草包围,稻草的形状是矩形,稻草人的形状也是矩形,相当于一个子问题

题解:考录一维的情况,若稻草人只有1格,稻草2格,C(n, 3);若稻草人大于1格,稻草2格,C(n,4);一共C(n, 3) + C(n, 4)

           推广到二维(C(n,3) + C(n,4))*(C(m, 3)+C(m,4))

#include 

using namespace std;

typedef unsigned long long ll;
const ll mod = 1e9 + 7;
ll n , m;

ll C(ll n, ll m){
    ll c = 1ll;
    if (n < m){
        return 0ll;
    }
    else if (n == m || m == 0) return 1ll;
    ll cnt = 1;
    for (ll i = n - m + 1; i <= n; i++){
        c *= i;
        c /= cnt++;
    }
    return c % mod;
}

int main()
{
    int T;
    cin >> T;
    for (int t = 1; t <= T; t++){
        printf("Case %d: ", t);
        scanf("%lld%lld", &n, &m);
        if (n < 3 || m < 3){
            cout << 0 << endl;
        }
        else {
            cout << ((C(n, 4) + C(n, 3) % mod) * (C(m, 3) + C(m , 4) % mod) % mod ) << endl;
        }
    }
    return 0;
}

I - Cockroaches

题意:田里有一堆蟑螂,杀虫剂可以杀一整行和一整列,问最多能杀多少蟑螂,有多少种杀法,定义杀法不同为,杀的蟑螂的集合不同,即至少有一个蟑螂不同

题解:特判每一行每一列只有一个蟑螂的情况

将蟑螂最多的行和蟑螂最多的列和蟑螂第二多的行和蟑螂第二多的列找出来,然后两辆组合,若组合的焦点都蟑螂,则蟑螂总数会少一个,最后输出蟑螂最多的行与蟑螂最多的列的组合,若该组合总数为零,则输出比这个答案少1的总数

#include 
#define endl "\n"
#define fi first
#define se second
#define FOR(i,s,t) for(int i=(s);i<=(t);++i)
#define mem(a,b) memset(a,b,sizeof(a))
#define rush() int MYTESTNUM;cin>>MYTESTNUM;while(MYTESTNUM--)
#define debug(x) printf("%d\n",x)
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;

struct node{
    int x, y;
}a[maxn];
int cas, bestx, besty;
int n;
map X, Y;
ll numx1, numx2, numy1, numy2, num0, num1;
int main()
{
    rush(){
        X.clear();
        Y.clear();
        printf("Case %d: ", ++cas);
        scanf("%d", &n);
        FOR(i, 1, n) {
            scanf("%d%d", &a[i].x, &a[i].y);
            X[a[i].x]++;
            Y[a[i].y]++;
        }
        bestx = 0, besty = 0;
        FOR(i, 1, n){
            bestx = max(bestx, X[a[i].x]);
            besty = max(besty, Y[a[i].y]);
        }
        if (X.size() == 1|| Y.size() == 1){
            printf("%d %d\n", n, 1);
            continue;
        }
        if (bestx == 1 && besty == 1){
            printf("%d %lld\n", 2, 1ll * n * (n - 1) / 2);
            continue;
        }
        numx1 = numx2 = numy1 = numy2 = 0;
        for (auto i : X){
            if (i.second == bestx) numx1++;
            else if (i.second == bestx - 1) numx2++;
        }
        for (auto i : Y){
            if (i.second == besty) numy1++;
            else if (i.second == besty - 1) numy2++;
        }
        //cout << numx1 << " " << numx2 << " " << numy1 << " " << numy2 << endl;
        num0 = 1ll * numx1 * numy1; // 3 * 3
        num1 = 1ll * numx1 * numy2 + numx2 * numy1; // 3 * 1 + 1 * 3
        FOR(i, 1, n){
            if (bestx + besty == X[a[i].x] + Y[a[i].y]){
                num0--;
                num1++;
            }
            else if (bestx + besty - 1== X[a[i].x] + Y[a[i].y]){
                num1--;
            }
        }
        //cout << bestx << " " << besty << endl;
        if (num0 == 0){
            printf("%d %lld\n", bestx + besty - 1, num1);
        }
        else{
            printf("%d %lld\n", bestx + besty, num0);
        }
    }

    return 0;
}

L - Ultra Weak Goldbach's Conjecture

 题意:给出一个数,将这个数分解场6格素数

题解:若这个数为偶数,分解为2 2 2 2 和n-8分解成的两个数

           若这个数为偶数,分解为2 2 2 3 和n-9分解成的两个数

#include 
using namespace std;
const int maxn = 1e8 + 5;
typedef long long ll;
bool isPrime[maxn];
vector prime;

bool _isPrime(ll key) {
    for (ll i = 2; i * i <= key; ++i) {
        if (key % i == 0) return false;
    }
    return true;
}

void pri() {
    memset(isPrime, true, sizeof(isPrime));
    for (ll i = 2; i < maxn; ++i) {
        if (isPrime[i]) {
            prime.push_back(i);
            for (ll j = i * i; j < maxn; j += i) isPrime[j] = false;
        }
    }
}

void solve(ll x) {
    for (int i = 0 ; i < prime.size(); i++) {
        int p = prime[i];
        if (_isPrime(x - p)) {
            cout << p << " " << x - p << endl;
            break;
        }
    }
}

int main() {
    pri();
    int t; cin >> t;
    for (int Case = 1; Case <= t; ++Case) {
        ll n; cin >> n;
        cout << "Case " << Case << ": ";
        if (n < 12) {
            cout << "IMPOSSIBLE" << endl;
            continue;
        }
        if (n & 1) printf("2 3 2 2 "), n -= 9;
        else printf("2 2 2 2 "), n -= 8;
        solve(n);
    }
    return 0;
}

 

你可能感兴趣的:(acm)