20180806 提高Day1 训练赛
题目目录
扫雷
题意
一个 $3$ 行 $N$ 列的扫雷,已知第二行没有雷,而且你知道第二行上面的数字,求有几种合法的埋雷方案。
$T$ 组数据,$N\leq 10000, T\leq 100$ 。答案模 $10^9+7$ 。
题解
DP。 设f[i][j]
表示当前在第 $i$ 列,$j$ 是一个四位状态,表示我的左上角,我的左下角,我的上面,我的下面有没有雷(这里我代表第二行第 $i$ 列)。
那么,我们枚举我的右上角和右下角放不放,然后判断一下是否合法,dp过去就可以了。
调试记录
- 初始化错了…
- 最后枚举到了 $n$ 而不是 $n-1$ ,导致多放置了两个雷。
代码
#pragma GCC optimize("-fdelete-null-pointer-checks,inline-functions-called-once,-funsafe-loop-optimizations,-fexpensive-optimizations,-foptimize-sibling-calls,-ftree-switch-conversion,-finline-small-functions,inline-small-functions,-frerun-cse-after-loop,-fhoist-adjacent-loads,-findirect-inlining,-freorder-functions,no-stack-protector,-fpartial-inlining,-fsched-interblock,-fcse-follow-jumps,-fcse-skip-blocks,-falign-functions,-fstrict-overflow,-fstrict-aliasing,-fschedule-insns2,-ftree-tail-merge,inline-functions,-fschedule-insns,-freorder-blocks,-fwhole-program,-funroll-loops,-fthread-jumps,-fcrossjumping,-fcaller-saves,-fdevirtualize,-falign-labels,-falign-loops,-falign-jumps,unroll-loops,-fsched-spec,-ffast-math,Ofast,inline,-fgcse,-fgcse-lm,-fipa-sra,-ftree-pre,-ftree-vrp,-fpeephole2",3) #include <bits/stdc++.h> using namespace std; #ifdef _WIN32 #define LL "%I64d" #else #define LL "%lld" #endif inline int rd() { int ret=0,nag=0;char ch;while(!isdigit(ch=getchar()))nag=ch=='-';ret=ch-'0';while(isdigit(ch=getchar()))(ret*=10)+=ch-'0';return nag?-ret:ret;} inline int rds(char*s) {char*p=s,ch;while(!isdigit(ch=getchar()));*(p++)=ch;while(isdigit(ch=getchar()))*(p++)=ch;return p-s;} typedef long long ll; ll f[10005][16]; char s[10005]; int count(int x) { int ret=0; while(x) { ret+=x&1; x >>= 1; } return ret; } int main() { int T = rd(); while (T--) { memset(f, 0, sizeof(f)); int n = rds(s); for (int sta=0; sta<4; sta++) f[1][sta] = count(sta)<=(s[0]-'0'); for (int i=1; i<=n; i++) for (int sta=0; sta<16; sta++) { for (int plc=0; plc<(i==n?1:4); plc++) { if (count(sta) + count(plc) != (s[i-1] - '0')) continue; (f[i+1][((sta & 0x3) << 2) | plc] += f[i][sta]) %= (ll)1e9+7; } } ll ans=0; for (int sta=0; sta<16; sta++) { // cerr << "place " << sta << " can get " << f[n+1][sta] << endl; (ans += f[n+1][sta]) %= (ll)1e9+7; } printf(LL "\n", ans); } return 0; }
最大公约数
题意
题解
首先,我们把 $a_i$ 分解质因数。
$\gcd\left(a_i, g\right)>1$,就是说,$a_i$ 和 $g$ 有至少相同的 $1$ 个质因数。
因此,我们把 $a_i$ 分解质因数,然后按因子分类。
询问的时候,我们把 $g$ 分解质因数,然后在每一种质因数内二分左端点和右端点。
现在的问题是,如何快速统计二分出来的区间内的最大值和最大值个数?
最大值我想到了RMQ,但是它不能统计最大值个数。我们可以在max的基础上再记一个sum,然后就可以统计最大值个数了。
调试记录
- $g$ 和 $a_i$ 可能有多个相同的质因数,但是这些质因数里的 $a_i$ 只算一次。我考试时没有考虑这种情况。
代码
#pragma GCC optimize("-fdelete-null-pointer-checks,inline-functions-called-once,-funsafe-loop-optimizations,-fexpensive-optimizations,-foptimize-sibling-calls,-ftree-switch-conversion,-finline-small-functions,inline-small-functions,-frerun-cse-after-loop,-fhoist-adjacent-loads,-findirect-inlining,-freorder-functions,no-stack-protector,-fpartial-inlining,-fsched-interblock,-fcse-follow-jumps,-fcse-skip-blocks,-falign-functions,-fstrict-overflow,-fstrict-aliasing,-fschedule-insns2,-ftree-tail-merge,inline-functions,-fschedule-insns,-freorder-blocks,-fwhole-program,-funroll-loops,-fthread-jumps,-fcrossjumping,-fcaller-saves,-fdevirtualize,-falign-labels,-falign-loops,-falign-jumps,unroll-loops,-fsched-spec,-ffast-math,Ofast,inline,-fgcse,-fgcse-lm,-fipa-sra,-ftree-pre,-ftree-vrp,-fpeephole2",3) #include <bits/stdc++.h> #define up(a, b) a = max(a, b) #define down(a, b) a = min(a, b) using namespace std; ifstream fin("gcd.in"); ofstream fout("gcd.out"); struct node { int v, id; node(int x, int y) : v(x), id(y) {} inline bool operator < (int res) { return id < res; } friend inline bool operator < (int res, const node & ref) { return res < ref.id; } }; struct wrapper { int a[20]; inline int & operator[] (int xb) { return a[xb]; } inline const int & operator[] (int xb) const { return a[xb]; } }; int n, m; vector<node> A[100005]; vector<wrapper> dp[100005]; vector<wrapper> sum[100005]; vector<int> F; map<int, bool> E; int mm[100005]; const int prime[] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313}; inline void fracit(int x, int id) { int b = x; for (int i=0; prime[i] <= sqrt(x); ++i) { int p = prime[i]; if (x % p == 0) { while (x % p == 0) x /= p; if (!(E[p])) E[p] = 1, F.push_back(p); A[p].push_back(node(b, id)); } } if (x > 1) { int p = x; if (!(E[p])) E[p] = 1, F.push_back(p); A[p].push_back(node(b, id)); } } inline void rmqit() { mm[0] = -1; for (int i=1; i<=::n; i++) { mm[i] = ((i & (i-1)) == 0) ? mm[i-1] + 1 : mm[i-1]; } for (int i=0; i<F.size(); i++) { vector<node> & A = ::A[F[i]]; vector<wrapper> & dp = ::dp[F[i]]; vector<wrapper> & sum = ::sum[F[i]]; int n = A.size(); dp.resize(n + 1); sum.resize(n + 1); for (int i=1; i<=n; i++) dp[i][0] = A[i-1].v, sum[i][0] = 1; for (int j=1; j<=mm[n]; j++) for (int i=1; i+(1<<j)-1<=n; i++) { if (dp[i][j-1] > dp[i+(1<<(j-1))][j-1]) { dp[i][j] = dp[i][j-1]; sum[i][j] = sum[i][j-1]; } else if (dp[i][j-1] < dp[i+(1<<(j-1))][j-1]) { dp[i][j] = dp[i+(1<<(j-1))][j-1]; sum[i][j] = sum[i+(1<<(j-1))][j-1]; } else { dp[i][j] = dp[i][j-1]; sum[i][j] = sum[i][j-1] + sum[i+(1<<(j-1))][j-1]; } } } } inline int rmq(int P, int x, int y) { int k = mm[y-x+1]; return max(dp[P][x][k],dp[P][y-(1<<k)+1][k]); } struct fakepair { int a,b; }; inline fakepair count(int P, int x, int y) { if (x <= y) { int k = mm[y-x+1]; fakepair ret = count(P, x+(1<<k), y); int u = dp[P][x][k], v = ret.a; if (u > v) return (fakepair){u, sum[P][x][k]}; else if (u < v) return (fakepair){v, ret.b}; else return (fakepair){u, sum[P][x][k] + ret.b}; } return (fakepair){-1, -1}; } inline void answer(int P, int l, int r, int & mx, int & sm) { if (A[P].empty()) return; vector<node> :: iterator it = lower_bound(A[P].begin(), A[P].end(), l); if (it == A[P].end()) return; int sp = it - A[P].begin() + 1; it = upper_bound(A[P].begin(), A[P].end(), r); int ep = it - A[P].begin(); if (sp > ep) return; int lcm = rmq(P, sp, ep); if (lcm > mx) mx = lcm, sm = count(P, sp, ep).b; } int main() { ios :: sync_with_stdio(false); fin >> n >> m; for (int i=1; i<=n; i++) { int x; fin >> x; fracit(x, i); } rmqit(); while (m--) { int g, l, r; fin >> g >> l >> r; int mx=-1, sum=-1; for (int i=0; prime[i]<=sqrt(g); i++) { int p = prime[i]; if (g % p == 0) { while (g % p == 0) g /= p; answer(p, l, r, mx, sum); } } if (g > 1) answer(g, l, r, mx, sum); fout << mx << " " << sum << endl; } return 0; }
随机排列
题意
代码
#include <bits/stdc++.h> #define pn(i) (i * (i-1) / 2) using namespace std; typedef long double ld; ld p[105][10005], g[105][10005]; ld P[105][10005], S[105][10005]; int a[105]; int main() { ios :: sync_with_stdio(false); p[1][0] = 1; for (int i=1; i<=100; i++) for (int j=0; j<=pn(i); j++) for (int k=0; k<=i; k++) p[i+1][j+k] += p[i][j] / (i+1); for (int i=1; i<=100; i++) { P[i][0] = p[i][0]; S[1][0] = 0; for (int j=1; j<=pn(i); j++) { P[i][j] = P[i][j-1] + p[i][j]; S[i][j] = S[i][j-1] + p[i][j] * j; } } for (int i=1; i<=100; i++) { g[i][0] = S[i][pn(i)]; for (int j=1; j<=pn(i); j++) { int k = floor(j + g[i][j-1]); if (k > pn(i)) k = pn(i); g[i][j] += S[i][k] - S[i][j]; g[i][j] -= (P[i][k] - P[i][j]) * j; g[i][j] += g[i][j-1] * (P[i][pn(i)] - P[i][k]); } } int T; cin >> T; while (T--) { int n, k; cin >> n >> k; for (int i=1; i<=n; i++) cin >> a[i]; int rep = 0; for (int i=1; i<n; i++) for (int j=i+1; j<=n; j++) rep += a[i] > a[j]; int oc = max(rep - k, 0); ld co = g[n][min(k-1, pn(n))]; if (oc <= co) { cout << oc << ".0000000000\n"; } else { cout << fixed << setprecision(10) << max((ld)0, co) << endl; } } return 0; }