• 2021CCPC河南省赛


    最终A了8道题, 河南省排名第四, 喜提一金, 也是在意料之中。 第一次三个队友集中在一起打比赛, 也体验了一下线下的氛围, 还是比较赞的, 自己也不是说毫无作用, 帮助团队做了几道题, 还是挺满意的。

    题目的下载地址

    1002

    emmmmm, 我当时是正着看的题, 所以率先看到了这道题, 我感觉是能写, 但刚开始这道题A的并不多, 并且我的数论就。。。。, 最后让给了队友, 最后还是A掉了。 首先我们看到, 总的方案数肯定是(m^n), 这就是最后的分母,在看我们对boss的伤害其实对技能没有关系, 只是跟技能的次数有关系,所以我们不妨枚举每个技能出现的次数所对boss造成的伤害, 最终乘以m即可。 假如第一个技能用了i次, 那能够造成的伤害总量是多少呢。 首先这个技能位置的选择是(C_n^i), 其他技能的选择就是((m-1)^{n-i}), 不难看出,这个技能对答案的贡献是(sum_{i = 1}^nC_n^i*(m-1)^{n-i}*i^2), 当然, 把这个数乘以m就是我们的最终答案。

    点击查看代码
    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define int long long
    const int mod = 1e9 + 7;
    const int N = 1e5 + 10;
    const int maxn = 1e5; 
    
    template < typename T > inline void read(T &x) {
    	x = 0; T ff = 1, ch = getchar();
    	while (!isdigit(ch)) {
    		if (ch == '-') ff = -1;
    		ch = getchar();
    	}
    	while (isdigit(ch)) {
    		x = (x << 1) + (x << 3) + (ch ^ 48);
    		ch = getchar();
    	}
    	x *= ff;
    }
    
    int T, n, m, ans = 0;
    int jc[N], inv[N];
    
    inline int power(int a, int b) {
    	int ans = 1;
    	while (b) {
    		if (b & 1) ans = (ans * a) % mod;
    		a = a * a % mod;
    		b >>= 1; 
    	}
    	return ans;
    }
    
    inline void pre() {
    	jc[0] = 1;
    	for (int i = 1; i <= maxn; ++i) jc[i] = jc[i - 1] * i % mod;
    	inv[maxn] = power(jc[maxn], mod - 2);
    	for (int i = maxn - 1; i >= 0; --i)
    		inv[i] = inv[i + 1] * (i + 1) % mod;
    }
    
    inline int C(int n, int m) {
    	return jc[n] * inv[m] % mod * inv[n - m] % mod;
    }
    
    signed main() {
    	read(T);
    	pre();
    	while (T--) {
    		read(n), read(m);
    		ans = 0;
    		for (int i = 1; i <= n; ++i) 
    			ans = (ans + m * C(n, i) % mod * power(m - 1, n - i) % mod * i % mod * i % mod) % mod;
    		ans = ans * power(power(m, n), mod - 2) % mod;
    		printf("%lld
    ", ans);
    	}
    	return 0;
    } 
    

    1003

    这是我独立A掉的一道题, 感觉还可以, 看到这张图上只有17个点, 考虑状压, 并且最优情况下的话, 每一次都会投出6, 结合最短路, 设dis[x][z],表示在第x个点, 状压的状态为z的所剩的最大骰子数。 跑一遍Dijkstra即可, 当然, 这道题的细节很多, 首先, dis的初值一定要是-1, 因为有些点根本就达不到, 并且, 题目上说的是严格大于这个格子的数值。 最后, 在你占领某个格子以后显然可以随意的来回, 不需要消耗糖果。 这个问题当时卡了我一下。最后我想出办法, 当我把队头的某个点取出后, 枚举当前所占领的点, 如果此时的dis[y][z]还没有更新的话, 我们可以让他回去, 及dis[y][z] = dis[x][z], 这样便于更新接下来答案。

    1005

    这是开的第一题, 也经历一些波折之后写过去了, 其实还是挺简单的, 设dp[i][j]表示前i个事件所剩体力为j所能得到的最大价值, 然后双重循环即可, 如果结束的话就是在每回合直接更新一下ans, 就相当于到这一轮结束了, 这道题要用滚动数组, 而且初始化一定要是最小值, 而不是0

    点击查看代码
    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define int long long
    const int N = 6e3 + 10;
    
    template < typename T > inline void read(T &x) {
    	x = 0; T ff = 1, ch = getchar();
    	while (!isdigit(ch)) {
    		if (ch == '-') ff = -1;
    		ch = getchar();
    	}
    	while (isdigit(ch)) {
    		x = (x << 1) + (x << 3) + (ch ^ 48);
    		ch = getchar();
    	}
    	x *= ff;
    }
    
    int T, n, H, ans;
    int a[N], b[N], c[N], d[N];
    int f[2][N];
    
    signed main() {
    	read(T);
    	while (T--) {
    		read(n), read(H);
    		for (int i = 1; i <= n; ++i) 
    			read(a[i]), read(b[i]), read(c[i]), read(d[i]);
    		memset(f, 0xcf, sizeof(f));
    		f[0][0] = 0; ans = 0;
    		int u = 1;
    		for (int i = 1; i <= n; ++i) {
    			for (int j = H; j >= 0; --j) {
    				if (j >= a[i]) f[u][j] = max(f[u][j], f[u ^ 1][j - a[i]] + b[i]);
    				if (j >= c[i]) f[u][j] = max(f[u][j], f[u ^ 1][j - c[i]] + d[i]);
    				ans = max(ans, f[u][j]);
    			}
    			memset(f[u ^ 1], 0xcf, sizeof(f[u ^ 1]));
    			u ^= 1;
    		}
    		printf("%lld
    ", ans);
    	}
    	return 0;
    }
    

    1006

    额。。。这也是我先看到的题, 我不知道怎么想的, 觉得这道题可写, 但是确实没啥思路。。。到最后竟然没有人写出来, 我还是先说一下思路把(能不能写出来还是一个问题。。。)通过打表或者猜测得知, 这个题的答案不会很大, 最大大概是17(当时应该想到的。。), 那么一定有一个矩形的差是不超过8的, 知道答案很小之后就可以枚举了!于是可以枚举小的那个矩阵的长宽的差值x和宽的值w ,那么这个矩形的长显然是x+w,由于面积不超过n,所以这个枚举的次数是(8sqrt{n})的枚举了其中一个矩阵,就知道了另一个矩阵的面积(S),由于已经知道答案会很小,另一个矩阵的长宽之差肯定也很小,枚举矩阵的宽(从(sqrt{S})逐渐减小的枚举),当长宽之差不小于lim- x就可以停止了,后面肯定没有要找的答案。加上一系列的优化应该会跑的很快。(这道题暂时待定把。。。。)

    1007

    这道题没写出来挺可惜的其实, 大概的思路都想到了其实, 就是说让每个点都作为中心点, 然后主副对角线不同元素取个最小值。 但是在统计的时候没有细想,哎。不想打了, 直接粘一波题解把。。
    image
    其实统计个数的时候完全可以开个数组来判断某个数是否出现过, 只是当时想的是行枚举, 其实按对角线枚举的话就轻而易举了。

    点击查看代码
    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    const int INF = 0x3f3f3f3f;
    const int N = 1e3 + 10;
    const int M = 1e6 + 10;
    
    template < typename T > inline void read(T &x) {
    	x = 0; T ff = 1, ch = getchar(); 
    	while (!isdigit(ch)) {
    		if (ch == '-') ff = -1;
    		ch = getchar();
    	} 
    	while (isdigit(ch)) {
    		x = (x << 1) + (x << 3) + (ch ^ 48);
    		ch = getchar();
    	}
    	x *= ff;
    }
    
    ll ans = 0;
    int T, n, a[N][N];
    int l[N][N], r[N][N]; //×óбºÍÓÒб 
    int cnt[M];
    
    inline bool check1(int x, int y, int v) {
    	if (x - v <= 0 || x + v > n || y - v <= 0 || y + v > n) return false;
    	if (cnt[a[x - v][y - v]] || cnt[a[x + v][y + v]]) return false;
    	if (a[x - v][y - v] == a[x + v][y + v]) return false;
    	++cnt[a[x - v][y - v]];
    	++cnt[a[x + v][y + v]];
    	return true;
    }
    
    inline bool check2(int x, int y, int v) {
    	if (x - v <= 0 || x + v > n || y - v <= 0 || y + v > n) return false;
    //	printf("x = %d y = %d v = %d
    ", x, y, v);
    //	printf("%d %d
    ", cnt[a[x + v][y - v]], cnt[a[x - v][y + v]]);
    	if (cnt[a[x + v][y - v]] || cnt[a[x - v][y + v]]) return false;
    	if (a[x + v][y - v] == a[x - v][y + v]) return false;
    	++cnt[a[x + v][y - v]];
    	++cnt[a[x - v][y + v]];
    	return true;
    }
    
    inline void solve1(int x, int y) {
    	++cnt[a[x][y]];
    	++x, ++y;
    	while (x <= n && y <= n) {
    //	printf("x = %d y = %d
    ", x, y);
    //	printf("cnt[1] = %d
    ", cnt[1]);
    		if (r[x - 1][y - 1] == 1) {
    			++cnt[a[x][y]];
    			int v = 1;
    			--cnt[a[x - 1][y - 1]];
    			while (check1(x, y, v)) ++v;
    			r[x][y] = v;
    		} else {
    			int v = r[x - 1][y - 1] - 1;
    			--cnt[a[x - v][y - v]];
    			--cnt[a[x - v - 1][y - v - 1]];
    			while (check1(x, y, v)) ++v;
    			r[x][y] = v;
    		}
    		++x, ++y;
    	}	
    	--x, --y;
    	--cnt[a[x][y]];
    }
    
    inline bool solve2(int x, int y) {
    	++cnt[a[x][y]];
    	++x, --y;
    	while (x <= n && y <= n && x > 0 && y > 0) {
    		if (l[x - 1][y + 1] == 1) {
    			++cnt[a[x][y]];
    			int v = 1;
    			--cnt[a[x - 1][y + 1]];
    			while (check2(x, y, v)) ++v;
    			l[x][y] = v;
    		} else {
    			int v = l[x - 1][y + 1] - 1;
    			--cnt[a[x - v][y + v]];
    			--cnt[a[x - v - 1][y + v + 1]];
    			while (check2(x, y, v)) ++v;
    			l[x][y] = v;
    		}
    		++x, --y;
    	}
    	--x, ++y;
    	--cnt[a[x][y]];
    }
    
    int main() {
    	read(T);
    	while (T--) {
    		read(n);
    		ans = 0;
    		for (int i = 1; i <= n; ++i) 
    			for (int j = 1; j <= n; ++j) {
    				read(a[i][j]);
    				l[i][j] = r[i][j] = 1;
    			}
    		solve1(1, 1); // ÓÒб 
    		for (int i = 2; i <= n; ++i) {
    			solve1(1, i);
    			solve1(i, 1);
    		}
    		solve2(1, 1); //×óб
    		for (int i = 2; i <= n; ++i) {
    			solve2(1, i);
    			solve2(i, n);
    		} 
    		for (int i = 1; i <= n; ++i) 
    			for (int j = 1; j <= n; ++j) {
    				ans += min(l[i][j], r[i][j]);
    //				printf("l[%d][%d] = %d
    ", i, j, l[i][j]);
    //				printf("r[%d][%d] = %d
    ", i, j, r[i][j]);
    			}
    		printf("%d
    ", ans);
    	}
    	return 0;
    }
    

    1008

    又是一道数论题, 当然是交给圣元了, 但是他的那种方法好像被卡了, 最后也没有过。 这道题的方法还是挺多的, 数论也要好好学呀。。。
    介于我数论的垃圾性, 我现在只学了一种做法, 还是写出来把。。
    首先当x>y时,x%y=k显然等价于x=ny+k;(n≥1)那么我们想到, 对于每个y和k,都有固定的x与之对应, 比如y+k, 2y+k...那么我们可以枚举出每一个k和y的x的取值, 复杂度为(O(k*n*sum_{x=1}^nfrac{n}{x})), 由著名的调和级数得知, 复杂度为(O(knlogn)), 是可以过去的,我们枚举每一种方案数是, 可以用数组f[i][j]表示k为i, x为j的方案数, 那么求一个前缀和就是n为j的方案数。
    再想一下, 当x<y时,如果k=0, 显然无解, 当k>0时, x只能是k, y可以取[k+1, n]之间的任意数, 特判一下即可。。

    点击查看代码
    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define int long long
    const int N = 1e5 + 10;
    const int maxn = 1e5;
    const int mod = 23333;
    
    template < typename T > inline void read(T &x) {
    	x = 0; T ff = 1, ch = getchar();
    	while (!isdigit(ch)) {
    		if (ch == '-') ff = -1;
    		ch = getchar();
    	}
    	while (isdigit(ch)) {
    		x = (x << 1) + (x << 3) + (ch ^ 48);
    		ch = getchar();
    	}
    	x *= ff;
    }
    
    int T, n, k, ans = 0;
    int f[11][N];
    
    inline int power(int a, int b) {
    	int ans = 1;
    	while (b) {
    		if (b & 1) ans = ans * a % mod;
    		a = a * a % mod;
    		b >>= 1;
    	}
    	return ans;
    }
    
    signed main() {
    	// x % y == k
    	for (int i = 0; i <= 10; ++i) { // 枚举k 
    		for (int j = i + 1; j <= maxn; ++j) // 枚举 y
    			for (int k = 1; k * j + i <= maxn; ++k) // k*j+i是x 
    				++f[i][k * j + i];
    		for (int j = 1; j <= maxn; ++j) f[i][j] = (f[i][j] + f[i][j - 1]) % mod;
    	}
    	//当x>=k时,x%y==k等价(x-k)%y==0 
    	//上面都是保证x>y的 
    	read(T);
    	while (T--) {
    		read(n), read(k);
    		int cnt = f[k][n];
    		if (k != 0) cnt += max(n - k, 0ll); 
    		// 这里表示当x<y时,x只能时k, y可以是[k+1, n]之间的人任何数
    		cnt %= mod;
    		cnt = cnt * power(n * n % mod, mod - 2) % mod;
    		ans ^= cnt;
    	}
    	printf("%lld
    ", ans);
    	return 0;
    }
    
    

    1009

    我和古晨峰合伙过的一道题, 首先对于每个背包i,都有l[i]的物品是必取的, 那我们显然是需要把这些物品全取出来, 把前k个值翻倍(不足k个全部翻倍), 这就是取最少物品的答案, 而对于每个背包i而言, 还可以取r[i]-l[i]个物品, 那我们把这些物品全部取出来, 从大到小排序, 每次取出最大的数显然就是当前方案的最大值, 这时, 我们需要动态维护一个前K大的值, 因为这些物品需要翻倍, 一个小根堆显然可以, 保证这个小根堆的size要≤k, 如果等于k的话, 就把最小的值取出,与当前的这个数比较, 如果当前的这个数比较大, 就把堆顶出栈, 这个数加进去, 还是挺容易实现的。

    1010

    额。。我和古晨峰神奇的讨论出主席树+二分的做法, 交了一发还真过了, 他的码力是真强呀。。。。
    先说一下我们的做法把, 首先离散化, 然后建一颗权值主席树, 从当前这个点去判断左边是否存在k个比它大的点, 如果存在的话就去二分这个节点, 总复杂度(nlog^2n), 与k没有关系。。,但是k只有50, 并且数据是随机生成的, 所以暴力出奇迹, 暴力直接A

    暴力
    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e5 + 10;
    
    template < typename T > inline void read(T &x) {
    	x = 0; T ff = 1, ch = getchar();
    	while (!isdigit(ch)) {
    		if (ch == '-') ff = -1;
    		ch = getchar();
    	}
    	while (isdigit(ch)) {
    		x = (x << 1) + (x << 3) + (ch ^ 48);
    		ch = getchar();
    	}
    	x *= ff;
    }
    
    int T, n, k, a[N];
    
    int main() {
    	read(T);
    	while (T--) {
    		read(n), read(k);
    		for (int i = 1; i <= n; ++i) read(a[i]);
    		for (int i = 1; i <= n; ++i) { 
    			if (i <= k) puts("-1");
    			else {
    				int cnt = 0;
    				for (int j = i - 1; j >= 0; --j) {
    					if (j + cnt < k) {
    						puts("-1");
    						break;
    					}
    					if (a[j] > a[i]) ++cnt;
    					if (cnt == k) {
    						printf("%d
    ", a[j]);
    						break;
    					}
    				}
    			}
    		}
    	}
    	return 0;
    }
    
    主席树+二分
    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e5 + 10;
    
    template < typename T > inline void read(T &x) {
    	x = 0; T ff = 1, ch = getchar();
    	while (!isdigit(ch)) {
    		if (ch == '-') ff = -1;
    		ch = getchar();
    	}
    	while (isdigit(ch)) {
    		x = (x << 1) + (x << 3) + (ch ^ 48);
    		ch = getchar();
    	}
    	x *= ff;
    }
    
    int T, n, k, tot = 0, a[N], b[N], root[N];
    struct tree {
    	int l, r;
    	int cnt;
    }t[N * 21];
    
    inline int build(int l, int r) {
    	int p = ++tot;
    	t[p].l = t[p].r = t[p].cnt = 0; 
    	if (l == r) return p;
    	int mid = l + r >> 1;
    	t[p].l = build(l, mid);
    	t[p].r = build(mid + 1, r);
    	return p; 
    }
    
    inline int query(int p, int l, int r, int num) {
    	if (l == r) return t[p].cnt;
    	int mid = l + r >> 1;
    	int ans = 0;
    	if (mid <= num) return query(t[p].r, mid + 1, r, num);
    	else return query(t[p].l, l, mid, num) + t[t[p].r].cnt;
    } 
    
    inline int insert(int p, int l, int r, int k) {
    	int q = ++tot;
    	t[q] = t[p];
    	if (l == r) {
    		++t[q].cnt;
    		return q; 
    	}
    	int mid = l + r >> 1;
    	if (k <= mid) t[q].l = insert(t[p].l, l, mid, k);
    	else t[q].r = insert(t[p].r, mid + 1, r, k);
    	t[q].cnt = t[t[q].l].cnt + t[t[q].r].cnt;
    	return q;
    }
    
    int main() {
    	read(T);
    	while (T--) {
    		read(n), read(k);
    		tot = 0;
    		for (int i = 1; i <= n; ++i) {
    			read(a[i]);
    			b[i] = a[i];
    		}
    		sort(b + 1, b + n + 1);
    		int num = unique(b + 1, b + n + 1) - b - 1;
    		for (int i = 1; i <= n; ++i) 
    			a[i] = lower_bound(b + 1, b + num + 1, a[i]) - b;
    		root[0] = build(1, num); 
    		for (int i = 1; i <= n; ++i) {
    			int cnt = query(root[i - 1], 1, num, a[i]);
    			if (cnt < k) puts("-1");
    			else {
    				int l = 1, r = i - 1;
    				while (l < r) {
    					int mid = l + r >> 1;
    					if (cnt - query(root[mid], 1, num, a[i]) >= k) l = mid + 1;
    					else r = mid;
    				}
    				printf("%d
    ", b[a[l]]); 
    			}
    			root[i] = insert(root[i - 1], 1, num, a[i]);
    		}
    	}
    	return 0;
    }
    

    总的来说, 这次的CCPC还算可以, 希望能够好好补补自己知识的不足, 并且和队伍磨合的更好。
    我失去了天堂,但我不坠地狱, 加油!!

  • 相关阅读:
    Android 中Service生命周期
    Android开发中退出程序几种方法
    FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_REORDER_TO_FRONT用法
    【Java并发编程实战】-----synchronized
    The specified child already has a parent错误
    使用Ant打包工具 基本介绍
    what's WSDL
    XFire WebService demo
    jws webservice code
    axis、xfire、CXF 、JWS
  • 原文地址:https://www.cnblogs.com/AK-ls/p/15494401.html
Copyright © 2020-2023  润新知