选举
Description
Alice 和 Bob 要竞选魔方市市长。
有 n n n 个选民,编号为 1 ∼ n 1 \sim n 1 ∼ n ,它们中有的人支持 Alice ,有的人支持 Bob ,还有的人保持中立。
现在你需要把选民分成若干个区间,每个区间的长度在 [ l , r ] [l,r] [ l , r ] 中。如果一个区间中支持 Alice 的人比支持 Bob 的人多,那么 Alice 得一票,一个区间中支持 Bob 的人比支持 Alice 的人多,那么 Bob 得一票。
Alice 想要赢得选举,于是它请你合理地分区间,使得它获得的票数减去 Bob 获得的票数最大。
第一行三个整数 n n n , l l l , r r r ,第二行 n n n 个整数,每个整数为 1 , 0 1,0 1 , 0 或 − 1 −1 − 1 ,第 i i i 个数为 1 1 1 表示它支持 Alice ,为 − 1 −1 − 1 表示它支持 Bob ,为 0 0 0 表示它保持中立。
Output
行一个整数表示 Alice 获得的票数减去 Bob 获得的票数的最大值。如果没有合法的方案输出 Impossible。
Hint
1 ≤ n ≤ 1 0 6 , 1 ≤ l ≤ r ≤ n 1 \le n \le 10^6, 1 \le l \le r \le n 1 ≤ n ≤ 1 0 6 , 1 ≤ l ≤ r ≤ n
Solution
O ( n 2 ) O(n^2) O ( n 2 ) 的暴力 DP 很好想,考虑怎么去优化,因为权值有 0 , 1 , − 1 0,1,-1 0 , 1 , − 1 所以没法单调队列,考虑以前缀和为下标建值域线段树维护 max \max max ,可以通过控制区间来知道哪些 DP 值需要 + 1 / − 1 +1/-1 + 1/ − 1 ,时间复杂度 O ( n log n ) O(n \log n) O ( n log n ) 。
#include <bits/stdc++.h> using namespace std;const int N = 1e6 +10 ;int n, l, r;int a[N], sum[N];int f[N];namespace tr { struct node { int ls, rs; int max = -INT_MAX; } a[N*10 ]; int root, node_cnt; multiset<int > s[N*2 ]; void modify (int pos, int val, int type, int &i = root, int l = -1e6 , int r = 1e6 ) ; int query (int ql, int qr, int i = root, int l = -1e6 , int r = 1e6 ) ; } int type (int l, int r) ;int main () { cin >> n >> l >> r; for (int i = 1 ; i <= n; i++) cin >> a[i], sum[i] = sum[i - 1 ] + a[i]; memset (f, -0x3f , sizeof (f)); f[0 ] = 0 ; for (int i = l; i <= n; i++) { if (i-r > 0 ) tr::modify (sum[i-r-1 ], f[i-r-1 ], -1 ); tr::modify (sum[i-l], f[i-l], 1 ); f[i] = max (f[i], tr::query (-1e6 , sum[i]-1 )+1 ); f[i] = max (f[i], tr::query (sum[i], sum[i])); f[i] = max (f[i], tr::query (sum[i]+1 , 1e6 )-1 ); } if (f[n] < -n) cout << "Impossible" ; else cout << f[n] << "\n" ; } int type (int l, int r) { if (sum[r]-sum[l-1 ] > 0 ) return 1 ; else if (sum[r]-sum[l-1 ] == 0 ) return 0 ; else return -1 ; } namespace tr { #define ls(i) a[i].ls #define rs(i) a[i].rs #define lmid ((l+r)>>1) #define rmid ((l+r+2)>>1) void pushup (int i) { a[i].max = max (a[ls (i)].max, a[rs (i)].max); } void modify (int pos, int val, int type, int &i, int l, int r) { if (!i) i = ++node_cnt; if (l == r) { multiset<int > &si = s[N+l]; if (type == 1 ) si.insert (val); else si.erase (si.find (val)); a[i].max = si.empty () ? -INT_MAX : *si.rbegin (); return void (); } if (pos <= lmid) modify (pos, val, type, ls (i), l, lmid); if (pos >= rmid) modify (pos, val, type, rs (i), rmid, r); pushup (i); } int query (int ql, int qr, int i, int l, int r) { if (ql <= l and r <= qr) return a[i].max; int ans = -INT_MAX; if (ql <= lmid) ans = max (ans, query (ql, qr, ls (i), l, lmid)); if (qr >= rmid) ans = max (ans, query (ql, qr, rs (i), rmid, r)); return ans; } }
卡片
Description
小象有 n n n 张卡片,第 i i i 张卡片上有一个数字 a i a_i a i 。 Alice 和 Bob 轮流选择卡片 (不能重复),如果某次选择后已选择的卡片上的数字的最大公约数为 1 1 1 或者没有卡片可以选择,那么当前角色失败。
你需要求出:
1、如果双方都选择最优策略,谁会获胜;
2、如果双方都随机选取, Alice 获胜的概率。
第一行一个整数 t t t 表示数据组数。
每组数据第一行一个整数 n n n ,第二行 n n n 个整数 a 1 , a 2 , ⋯ , a n a_1, a_2, \cdots, a_n a 1 , a 2 , ⋯ , a n 。
Output
每组数据输出一行,包含一个整数和一个实数,用一个空格隔开。整数为 1 1 1 表示 Alice 获胜,0 0 0 表示 Bob 获胜。实数表示获胜概率,保留 4 4 4 位小数。
Hint
t ≤ 10 , n ≤ 100 , a i ≤ 1 0 5 t \le 10, n \le 100, a_i \le 10^5 t ≤ 10 , n ≤ 100 , a i ≤ 1 0 5
Solution
一个状态只与当前 gcd \gcd g cd 和已选卡片数量有关,记录这两个信息,并把每个 a i a_i a i 的约数拆开。
#include <bits/stdc++.h> using namespace std;#define gcd __gcd typedef double db;const int N = 105 ;const int A = 1e5 +10 ;int n, a[N], tmp[A];int f[A][N];db g[A][N]; void solve () ;int main () { int T; cin >> T; while (T --> 0 ) { memset (g, 0 , sizeof (g)); memset (f, 0 , sizeof (f)); memset (tmp, 0 , sizeof (tmp)); solve (); } } void solve () { cin >> n; for (int i = 1 ; i <= n; i++) cin >> a[i]; for (int i = 1 ; i <= n; i++) for (int j = 1 ; j*j <= a[i]; j++) if (a[i]%j == 0 ) { tmp[j]++; if (j*j != a[i]) tmp[a[i]/j]++; } for (int i = 1 ; i <= n; i++) f[1 ][i] = g[1 ][i] = 1 ; for (int i = 2 ; i <= 100000 ; i++) for (int j = tmp[i]; j >= 1 ; j--) { g[i][j] = (1 -g[i][j+1 ])*(tmp[i]-j); if (j != tmp[i]) f[i][j] = f[i][j+1 ]^1 ; for (int k = 1 ; k <= n; k++) if (a[k]%i != 0 ) { int v = gcd (a[k], i); g[i][j] += 1 -g[v][j+1 ]; f[i][j] |= f[v][j+1 ]^1 ; } if (j < n) g[i][j] /= n-j; } bool flag = false ; db ans = 0 ; for (int i = 1 ; i <= n; i++) { flag |= f[a[i]][1 ]^1 ; ans += (1 -g[a[i]][1 ])/n; } printf ("%d %.4f\n" , flag, ans); }