2018 CCPC 女生专场

可能是史上最弱的验题人——

Problem A

(小)模拟。

#include <bits/stdc++.h>

using namespace std;

int T;

int main(){

	scanf("%d", &T);
	while (T--){
		char rk[10];
		scanf("%s", rk);
		int n = strlen(rk);
		for (int i = 0; i < 3 - n; ++i)
			putchar(' ');
		printf("%s", rk);
		putchar('|');
		char S[20];
		scanf("%s", S);
		printf("%s", S);
		n = strlen(S);
		for (int i = n; i < 16; ++i)
			putchar(' ');
		putchar('|');
		scanf("%s", rk);
		printf("%s", rk);
		scanf("%s", rk);
		if (rk[0] == 'R' && rk[1] == 'u') {
			int x;
			scanf("%d", &x);
			putchar('|');
			putchar('[');
			for (int i = 1; i <= x; ++i)
				putchar('X');
			for (int i = 1; i <= 10 - x; ++i)
				putchar(' ');
			putchar(']');
		}
		else{
			if (rk[0] == 'F') {
				rk[0] = 'A';
				rk[1] = 'C';
				rk[2] = '*';
				rk[3] = '\0';
			}
			putchar('|');
			putchar('[');
			for (int i = 0; i < 4; ++i)
				putchar(' ');
			printf("%s", rk);
			int n = strlen(rk);
			for (int i = 1; i <= 6 - n; ++i)
				putchar(' ');
			putchar(']');
		}
		puts("");
	}
}

 

Problem B

观察到$d$大于$316$的质因子最多只有一个,那么先判掉$<= 316$的所有质因子,搞个前缀和就可以了。

然后特判大于$316$的质因子即可,方法有很多。

 

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)    for (int i(a); i <= (b); ++i)
#define dec(i, a, b)    for (int i(a); i >= (b); --i)
#define fi		first
#define se		second
#define MP		make_pair

typedef long long LL;
typedef pair <int, int> PII;

const int N = 1e5 + 10;

int T;
int s[70][N];
int a[N];
int n, m;
int re[N];
int c[N];
int id = 0;
int prime[N], fp[N], val[N];
vector <PII> pri[N];
vector <int> v[N];

int main(){

	rep(i, 2, 1e5){
		for (int j = i + i; j <= 1e5; j += i) c[j] = 1;

	}

	rep(i, 2, 1e5){
		if (!c[i]){
			++id;
			prime[id] = i;
			fp[i] = id;
		}
	}

	rep(i, 1, 1e5) val[i] = i;

	rep(i, 2, 1e5) if (!c[i]){
		for (int j = i + i; j <= 1e5; j += i){
			int cnt = 0;
			while (val[j] % i == 0) val[j] /= i, ++cnt;
			pri[j].push_back(MP(i, cnt));
			if (i > 316) re[j] = i;
		}
	}

	rep(i, 2, 1e5) if (val[i] > 1){
		pri[i].push_back(MP(i, 1));
		if (val[i] > 316) re[i] = val[i];
	}

	scanf("%d", &T);
	while (T--){
		scanf("%d%d", &n, &m);
		memset(s, 0, sizeof s);
		memset(a, 0, sizeof a);

		rep(i, 1, 1e5 + 1) v[i].clear();
		rep(i, 1, n){
			int x;
			scanf("%d", &x);
			for (auto u : pri[x]){
				int nowid = fp[u.fi], nowcnt = u.se;
				if (u.fi <= 316){
					s[nowid][i] += nowcnt;
				}
			}

			if (re[x]){
				a[i] = re[x];
				v[re[x]].push_back(i);
			}
		}

		rep(i, 1, 67){
			rep(j, 1, n) s[i][j] += s[i][j - 1];
		}

		while (m--){
			int x, y, z;
			scanf("%d%d%d", &x, &y, &z);

			int ret = 1;
			for (auto u : pri[z]){
				if (u.fi > 316) break;
				int nowid = fp[u.fi], nowcnt = u.se;
				ret &= (s[nowid][y] - s[nowid][x - 1] >= u.se);
			}

			if (re[z]){
				int num = re[z];
				int sz = (int)v[num].size();
				if (sz == 0) ret = 0;
				else{

					int l = 0, r = sz - 1;
					if (v[num][r] < x){
						ret = 0;
					}

					else{
						while (l + 1 < r){
							int mid = (l + r) >> 1;
							if (v[num][mid] >= x){
								r = mid;
							}

							else{
								l = mid + 1;
							}
						}

						int t;
						if (v[num][l] >= x) t = l;
						else t = r;

						int minpos = v[num][t];
						ret &= (minpos <= y);
					}
				}
			}

			puts(ret ? "Yes" : "No");    
		}
	}

	return 0;

}

 

 

 

 

Problem C

二分一下就可以了,具体实现的时候要尽量避开log函数,否则会因为精度问题产生误差。

 

#include <bits/stdc++.h>

using namespace std;

#define ll long long

ll LOG(ll x){
	ll ans = 0;
	while (true) {
		if (x == 1) return ans;
		else if (x == 2) return ans + 1;
		x = x - (x / 2);
		ans++;
	}
}

bool check(ll n, ll k, int a, int b) {
	ll m = LOG(n);
	if (m == 0) return 1;
	ll tmp = 1;
	while (a--) {
		if (k / tmp < n) return 0;
		tmp *= n;
	}
	while (b--) {
		if (k / tmp < m) return 0;
		tmp *= m;
	}
	return 1;
}

int a, b;
int T;
ll k;

int main() {
	

	scanf("%d", &T);

	while (T--) {
		scanf("%d%d%I64d", &a, &b, &k);
		ll l = 1, r = k, ans;
		while (l <= r) {
			ll mid = (l + r) / 2;
			if (check(mid, k, a, b)) ans = mid, l = mid + 1;
			else r = mid - 1;
		}
		printf("%I64d\n", ans);
	}
	return 0;
}

 

 

 

 

Problem D

难题。考虑DP,设$f[i][j][x][y]$表示走到第$i$行第$j$列,有$x$个路径上本来应该计入答案的格子没有计入答案,

有$y$个本不属于这条路径的格子计入了答案的最大值。

走到$a_{i,j}$的时候把那些肯定不可能走到的格子排序然后贪心转移即可。

 

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second


typedef long long LL;

const int N = 53;
const int K = 27;

int T;
int f[N][N][K][K], a[N][N], c[N];
int n, m, k;
int ans = 0;


inline void up(int &x, int y){
	if (x < y) x = y;
}

int main(){

	scanf("%d", &T);
	while (T--){
		scanf("%d%d%d", &n, &m, &k);
		rep(i, 1, n){
			rep(j, 1, m) scanf("%d", a[i] + j);
		}

		memset(f, 0xc0, sizeof f);
		
		f[1][1][0][0] = a[1][1];
		f[1][1][0][1] = 0;

		rep(i, 1, n){
			rep(j, 1, m){
				rep(q, 1, m){
					if (q != j){
						if (q < j){
							c[q] = a[i + 1][q];
						}

						else{
							c[q] = a[i][q];
						}
					}

					else{
						c[q] = 0;
					}
				}

				sort(c + 1, c + m + 1, greater<int>());

				rep(q, 1, m){
					c[q] += c[q - 1];
				}

				rep(p, 0, k){
					rep(q, 0, k){
						up(f[i][j + 1][p][q],     f[i][j][p][q] + a[i][j + 1]);
						up(f[i][j + 1][p][q + 1], f[i][j][p][q]);
						rep(z, 0, m - 1){
							if (p + z > k) break;
							up(f[i + 1][j][p + z][q],     f[i][j][p][q] + a[i + 1][j] + c[z]);
							up(f[i + 1][j][p + z][q + 1], f[i][j][p][q] + c[z]);
						}
					}
				}
			}
		}

		ans = 0;
		rep(i, 0, k) up(ans, f[n][m][i][i]);
		printf("%d\n", ans);
	}

	return 0;
}

 

 

 

 

Problem E

直接上最短路就可以了,注意计算对数要用整数运算,跑最短路要堆优化Dij

(不是这个算法的全被卡了)

 

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second


typedef long long LL;
typedef pair <int, int> PII;

const int N = 2e5 + 10;

struct node{
	int u;
	LL w;
	friend bool operator < (const node &a, const node &b){
		return a.w > b.w;
	}
};

int T;
int b[N];
int inqueue[N];
int n, m;
int ret;
LL d[N];
LL a[N];
vector <PII> v[N];

void dij(){
	priority_queue <node> q;
	static bool vis[N];
	rep(i, 1, n + 1) d[i] = 4e18, vis[i] = false;
	q.push({1, 1});
	d[1] = 1;

	while (!q.empty()){
		int u = q.top().u;
		q.pop();
		if (vis[u]) continue;
		vis[u] = 1;
		for (auto edge : v[u]){
			int xx = edge.fi, id = edge.se, wb = b[id];
			LL wa = a[id];
			
			if (d[u] + wa < d[xx] && (d[u] + wa) / d[u] >= (1ll << wb)){
				d[xx] = d[u] + wa;
				q.push({xx, d[xx]});
			}
		}
	}
}


int main(){

	scanf("%d", &T);
	while (T--){
		rep(i, 0, n + 1) v[i].clear();
		scanf("%d%d", &n, &m);
		rep(i, 1, m){
			int x, y;
			scanf("%d%d%lld%d", &x, &y, a + i, b + i);
			v[x].push_back(MP(y, i));
		}


		dij();

		if (d[n] == 4e18){
			puts("-1");
			continue;
		}

		ret = 0;
		while (d[n]){
			++ret;
			d[n] /= 2ll;
		}

		printf("%d\n", ret - 1);
	}

	return 0;
}

 

 

 

 

Problem F

勇敢地直接上树上莫队,对$01$数组分块,同时维护每个块内$0$的个数,然后注意一些细节,就可以AC了。

在HDOJ上大概要跑$10s$。

二分答案+主席树方法待补。

 

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)

const int N = 2e5 + 10;
const int A = 19;

int T;
int n, m;
int cnt, bs, g, top, ti, ret;
int a[N], b[N], c[N], bg[N];
int f[N][A], deep[N], dfn[N];
int belong[N], stk[N];
int vis[N], s[N], ans[N];
int L[N], R[N];
int bl[N], bss, blocknum;
int extra;
int exist[N];
vector <int> v[N];

struct node{
	int x, y, id;
	friend bool operator < (const node &a, const node &b){
		return belong[a.x] == belong[b.x] ? dfn[a.y] < dfn[b.y] : belong[a.x] < belong[b.x];
	}
} q[N];

int LCA(int a, int b){
	if (deep[a] < deep[b]) swap(a, b);
	for (int i = 0,  delta = deep[a] - deep[b]; delta; delta >>= 1, ++i)
		if (delta & 1) a = f[a][i];

	if (a == b) return a;
	dec(i, 18, 0) if (f[a][i] ^ f[b][i]){
		a = f[a][i];
		b = f[b][i];
	}
	return f[a][0];
}

void dfs(int x, int fa, int dep){
	deep[x] = dep;
	if (fa){
		f[x][0] = fa;
		for (int i = 0; f[f[x][i]][i]; ++i)
			f[x][i + 1] = f[f[x][i]][i];
	}

	for (auto u : v[x]){
		if (u == fa) continue;
		dfs(u, x, dep + 1);
	}
}

void dfs(int x, int fa){
	dfn[x] = ++ti;
	int bot = top;
	for (auto u : v[x]){
		if (u == fa) continue;
		dfs(u, x);
		if (top - bot >= bs){
			++g;
			while (top ^ bot) belong[stk[top--]] = g;
		}
	}
	stk[++top] = x;
}

void rev(int x){
	if (c[a[x]] == 0) --bg[bl[a[x]]];
	else ++bg[bl[a[x]]];	
	
	c[a[x]] ^= 1;
	vis[x] ^= 1;
}

void work(int x, int y){
	for (; x ^ y; ){
		if (deep[x] < deep[y]) swap(x, y);
		rev(x);
		x = f[x][0];
	}
}


void init(){
	rep(i, 0, n + 1) v[i].clear();
	memset(c, 0, sizeof c);
	memset(f, 0, sizeof f);
	memset(vis, 0, sizeof vis);
	memset(exist, 0, sizeof exist);
	ti  = 0;
	top = 0;
	g   = 0;
	cnt = 0;
	bs  = 0;
	ret = 0;
}

int main(){

	scanf("%d", &T);
	while (T--){
		scanf("%d%d", &n, &m);
		init();
		rep(i, 1, n) scanf("%d", a + i), b[i] = a[i];

		rep(i, 1, n) exist[a[i]] = 1;

		rep(i, 1, 200002) if (!exist[i]){
			extra = i;
			break;
		}


		sort(b + 1, b + n + 1);
		cnt = unique(b + 1, b + n + 1) - b - 1;
		rep(i, 1, n) a[i] = lower_bound(b + 1, b + cnt + 1, a[i]) - b;


		rep(i, 2, n){
			int x, y;
			scanf("%d%d", &x, &y);
			v[x].push_back(y);
			v[y].push_back(x);
		}

		dfs(1, 0, 0);
		bs = sqrt(n);
		bss = sqrt(cnt);
		dfs(1, 0);

		rep(i, 1, cnt) bl[i] = (i - 1) / bss + 1;
		blocknum = bl[cnt];

		rep(i, 1, blocknum) L[i] = 1e9, R[i] = 0;
		rep(i, 1, cnt){
			L[bl[i]] = min(L[bl[i]], i);
			R[bl[i]] = max(R[bl[i]], i);
		}

		rep(i, 1, blocknum) bg[i] = R[i] - L[i] + 1;


		rep(i, 1, m){
			scanf("%d%d", &q[i].x, &q[i].y);
			q[i].id = i;
			if (dfn[q[i].x] > dfn[q[i].y]) swap(q[i].x, q[i].y);
		}

		sort(q + 1, q + m + 1);
		q[0].x = q[0].y = 1;

		rep(i, 1, m){
			work(q[i - 1].x, q[i].x);
			work(q[i - 1].y, q[i].y);
			int lca = LCA(q[i].x, q[i].y);
			rev(lca);
			
			int now = 0, fg = 0, ret = 200001;
			rep(i, 1, blocknum){
				now += bg[i];
				if (now > 0){
					fg = 1;
					rep(j, L[i], R[i]) if (!c[j]){
						ret = j;
						break;
					}

					break;
				}
			}

			if (fg) ans[q[i].id] = min(b[ret], extra);
			else ans[q[i].id] = extra;

			rev(lca);
		}

		rep(i, 1, m) printf("%d\n", ans[i]);

	}
	return 0;
}

 

 

Problem G

全场题。

 

#include <bits/stdc++.h>

using namespace std;

const int N = 505, INF = 0x3f3f3f3f;

int t;

int main(){

	scanf("%d",&t);
	for (int id=1001;id<=1000+t;id++){
		int n,m; scanf("%d%d",&n,&m);
		int ans1=INF,ans2=INF;
		for (int i=1;i<=n;i++)
		{
			int x; scanf("%d",&x);
			ans1=min(ans1,x);
		}
		for (int i=1;i<=m;i++)
		{
			int x; scanf("%d",&x);
			ans2=min(ans2,x);
		}
		printf("Problem %d:\n",id);
		printf("Shortest judge solution: %d bytes.\n",ans1);
		if (ans2==INF)
			printf("Shortest team solution: N/A bytes.\n");
		else 
			printf("Shortest team solution: %d bytes.\n",ans2);
	}
}

 

 

 

 

Problem H

难题,题目给定的是一个二分图模型,

我们需要把这个二分图的模型转化成基环生成树森林。

然后从高位到低位分治,把当前集合根据最高位$0$或$1$分成两个不同的集合。

显然集合内部连边方案更优。可以证明当两个集合大小都超过$3$时,没有横跨集合的边。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second


typedef long long LL;

const int N = 3e5 + 10;

int T;
int n;

LL solve(int d, vector<int> a){
	if (d < 0) return 0;
	int n = (int)a.size();
	if (n <= 4){
		LL ret = 0;
		vector <int> c;
		rep(i, 0, n - 1) rep(j, i + 1, n - 1) c.push_back(a[i] ^ a[j]);
		sort(c.begin(), c.end());
		for (int i = 0; i < n && i < n * (n - 1) / 2; i++) ret += c[i];
		return ret;
	}

	vector <int> b[2];
	rep(i, 0, n - 1) b[a[i] >> d & 1].push_back(a[i]);
	a.clear();
	LL ret = solve(d - 1, b[0]) + solve(d - 1, b[1]);
	int t[2] = {(int)b[0].size(), (int)b[1].size()};
	if (t[0] && t[1] && (t[0] < 3 || t[1] < 3)){
		int mi = 2e9;
		rep(i, 0, t[0] - 1) rep(j, 0, t[1] - 1) mi = min(mi, b[0][i] ^ b[1][j]);
		ret += mi;
	}

	return ret;
}

int main(){
	
	scanf("%d", &T);

	while (T--){
		scanf("%d", &n);
		vector <int> a(n);
		rep(i, 0, n - 1) scanf("%d", &a[i]);
		printf("%lld\n", solve(29, a));
	}

	return 0;
}

 

 

Problem I

其实这题就没几行……完全不需要后缀数组。

直接从后往前扫一遍,递推就可以了。

 

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second


typedef long long LL;

const int N = 1e6 + 10;

int T;
int n;
char s[N], ans[N];


int main(){

	scanf("%d", &T);
	while (T--){
		scanf("%d", &n);
		scanf("%s", s + 1);

		ans[n] = '>';
		dec(i, n - 1, 1){
			if (s[i] < s[i + 1]) ans[i] = '<';
			else if (s[i] > s[i + 1]) ans[i] = '>';
			else ans[i] = ans[i + 1];
		}

		ans[n] = '\0';
		puts(ans + 1);
	}

	return 0;
}

 

 

 

 

Problem J

由于数据是随机的,所以直接枚举回文重心往两边不断延伸(也就是直接暴力)就可以了。

VP的时候直接尝试只枚举长度 $<= 3$的回文串,直接AC了。

赛前预计AC $≈$ $30$,赛场上去掉打星队AC $= 3$

……

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100005;

vector<int>link[N];
int a[N];
int cnt[N];
int t;

int main(){

	scanf("%d",&t);
	while(t--)
	{
		int n; scanf("%d",&n);
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		ll ans=n;
		for (int i=1;i<n;i++)
		{
			int x,y; scanf("%d%d",&x,&y);
			link[x].push_back(y);
			link[y].push_back(x);
			if (a[x]==a[y]) ans++;
		}
		for (int i=1;i<=n;i++)
		{
			for (int v:link[i])
			{
				ans+=cnt[a[v]];
				cnt[a[v]]++;
			}
			for (int v:link[i]) cnt[a[v]]--;
		}
		printf("%lld\n",ans);
		for (int i=1;i<=n;i++) link[i].clear();
	}
}

 

 

 

 

Problem K

难题。考虑容斥。

计数的时候对于某个格子,如何减掉重复计算的答案?

把这个矩阵往左,往上,往左上各推一个单位,就可以很巧妙地容斥了。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second


typedef long long LL;

const int N = 100005;
const int M = 1005;

inline LL calc(int n){ return 1ll * n * (n - 1) * (n - 2) / 6;}

int T;
int n;
int x[N][2], y[N][2];
int c[M][M];
LL  ret[4];

int main(){

	scanf("%d", &T);
	while (T--){
		scanf("%d", &n);
		rep(i, 1, n) rep(j, 0, 1) scanf("%d%d", x[i] + j, y[i] + j);
		memset(ret, 0, sizeof ret);
		rep(op, 0, 3){
			memset(c, 0, sizeof c);
			rep(i, 1, n){
				int sx = -(op / 2), sy = -(op % 2);
				c[x[i][0]][y[i][0]]++;
				c[x[i][0]][y[i][1] + sy + 1]--;
				c[x[i][1] + sx + 1][y[i][0]]--;
				c[x[i][1] + sx + 1][y[i][1] + sy + 1]++;
			}

			rep(i, 1, M - 1){
				rep(j, 1, M - 1){
					c[i][j] += c[i - 1][j] + c[i][j - 1] - c[i - 1][j - 1];
					ret[op] += calc(c[i][j]);
				}
			}
		}
		printf("%lld\n", ret[0] - ret[1] - ret[2] + ret[3]);
	}

	return 0;
}

 

转载于:https://www.cnblogs.com/cxhscst2/p/9102624.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值