https://www.luogu.org/contestnew/show/12006
我是比赛完后在去写的
这是我第一次打洛谷月赛,之前一次是比赛完才去看而且写了第一题就没写后面的了
284分,太水了,rank85左右
第一题第二题AC了,第三题写了3^n的算法,知道会超时,也知道满分做法应该是只考虑有价值的状态
但是还是没想出正解,拿了70分
第四题想到了讲评中说的错误的做法,然后细节比较多,怒刚1.5h,然而写挂了,交上去14分……
还不如写暴力有30~40分
考场策略出错了,如果最后一道题考虑骗分,能上300(然而300分rank还是很后)
P4994 终于结束的起点
5分钟AC。
签到题,数组都不用开。
写的时候不太确定会不会答案非常大导致超时,但是直接交就AC了
好像有个什么定理可以得出答案不会太大。反正我ac了就好
#include<bits/stdc++.h> #define REP(i, a, b) for(register int i = (a); i < (b); i++) #define _for(i, a, b) for(register int i = (a); i <= b; i++) using namespace std; int main() { int m; scanf("%d", &m); int a = 0, b = 1; int ans = 0; while(1) { ans++; int t = (a + b) % m; a = b; b = t; if(a == 0 && b == 1) { printf("%d\n", ans); break; } } return 0; }
P4995 跳跳!
30分钟AC
看完题脑中闪过贪心的念头,但是后来看到数据范围400,就去想n^3的dp了
然后最后折腾了一番,才发觉来回跳一定比顺着跳要优。
那是不是一直来回跳就是最优的
然后我就猜了最高,最低,次高,次低……
然后就AC了。
大胆猜想!!
#include<bits/stdc++.h> #define REP(i, a, b) for(register int i = (a); i < (b); i++) #define _for(i, a, b) for(register int i = (a); i <= b; i++) using namespace std; typedef long long ll; const int MAXN = 300 + 10; ll dp[MAXN], a[MAXN], ans; int n; int main() { scanf("%d", &n); _for(i, 1, n) scanf("%lld", &a[i]); a[0] = 0; sort(a, a + n + 1); int l = 0, r = n; while(l < r) { ans += (a[r] - a[l]) * (a[r] - a[l]); l++; if(l >= r) break; ans += (a[r] - a[l]) * (a[r] - a[l]); r--; } printf("%lld\n", ans); return 0; }
P4996 咕咕咕
1h10min拿了70分
第一反应就是枚举子集,3^n
但是自己没注意到一些细节,以及没想清楚当前这个状态的价值要乘上方案,所以一直调来调去
一个小时左右才过了样例
#include<bits/stdc++.h> #define add(a, b) a = (a + b) % mod #define REP(i, a, b) for(register int i = (a); i < (b); i++) #define _for(i, a, b) for(register int i = (a); i <= b; i++) using namespace std; typedef long long ll; const int mod = 998244353; const int MAXN = (1 << 20) + 10; int dp[MAXN], a[MAXN], num[MAXN], ans; char s[MAXN]; int n, m; int main() { scanf("%d%d", &n, &m); _for(i, 1, m) { int x = 0, y; scanf("%s%d", s, &y); REP(i, 0, strlen(s)) x = x * 2 + s[i] - '0'; a[x] = y; } dp[0] = a[0]; num[0] = 1; REP(S, 1, 1 << n) { for(int S0 = (S - 1) & S; ; S0 = (S0 - 1) & S) { add(dp[S], dp[S0]); add(num[S], num[S0]); if(!S0) break; } add(dp[S], 1ll * num[S] * a[S]); } printf("%d\n", dp[(1 << n) - 1]); return 0; }
显然会超时
可以只考虑有价值的状态对答案的贡献
显然把包含这个状态的方案数乘以这个状态的价值就是对答案的贡献
那么考虑怎么算方案
比如对于011
所有方案都是从000到011再到111
那么考虑从000到011的方案
显然是把两个0变成两个1的方案
更一般的来说,可以初始化出一个数组,num[i]表示把i个0变成i个1的方案数
考虑把j个0一起变成1
那么有c(i, j) * num[i-j]种方案
那么枚举j就可以了
现在想想其实不难,但是考试的时候就是没想到
做题不够多
#include<bits/stdc++.h> #define add(a, b) a = (a + b) % mod #define REP(i, a, b) for(register int i = (a); i < (b); i++) #define _for(i, a, b) for(register int i = (a); i <= b; i++) using namespace std; typedef long long ll; const int mod = 998244353; const int MAXN = 21; ll c[MAXN][MAXN], num[MAXN], ans; char s[MAXN]; int n, m; void init() { _for(i, 0, 20) { c[i][0] = 1; _for(j, 1, i) add(c[i][j], c[i-1][j-1] + c[i-1][j]); } num[0] = 1; _for(i, 1, 20) _for(j, 1, i) add(num[i], c[i][j] * num[i-j]); } int main() { init(); scanf("%d%d", &n, &m); _for(i, 1, m) { int x = 0, a, cnt = 0; scanf("%s%d", s, &a); REP(i, 0, strlen(s)) cnt += (s[i] == '1'); add(ans, a * num[cnt] % mod * num[n-cnt]); } printf("%lld\n", ans); return 0; }