第四周 3.20-3.26
3.20
HDU 5647 DZY Loves Connecting
不能直接逆元搞,因为可能会变成0。(据说可以特判一下。
然而直接dp[0]子树方案数,dp[1]子树贡献一次dp就可以了。
考虑当前节点新加一个孩子树,增加的贡献分两部分,
一部分是原来树节点的贡献,增加原来的答案×新增孩子树方案数,
另一部分是新增孩子树的贡献,增加原来方案数×新增孩子树答案。
1 #pragma comment(linker, "/STACK:102400000,102400000") 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 using namespace std; 7 typedef long long LL; 8 const LL mod = 1e9 + 7; 9 const int maxn = 2e5 + 10; 10 11 // tree 12 int cnt, h[maxn]; 13 struct edge 14 { 15 int to, pre; 16 } e[maxn<<1]; 17 18 void add(int from, int to) 19 { 20 cnt++; 21 e[cnt].pre = h[from]; 22 e[cnt].to = to; 23 h[from] = cnt; 24 } 25 26 void init() 27 { 28 cnt = 0; 29 memset(h, 0, sizeof(h)); 30 } 31 32 // dp 33 LL dp[maxn][2]; 34 void dfs(int x, int f) 35 { 36 dp[x][0] = dp[x][1] = 1; 37 for(int i = h[x]; i; i = e[i].pre) 38 { 39 int to = e[i].to; 40 if(to == f) continue; 41 dfs(to, x); 42 dp[x][1] = (dp[x][1] * (dp[to][0] + 1) + dp[x][0] * dp[to][1]) % mod; 43 dp[x][0] = dp[x][0] * (dp[to][0] + 1) % mod; 44 } 45 } 46 47 int main(void) 48 { 49 int T; 50 scanf("%d", &T); 51 while(T--) 52 { 53 init(); 54 int n; 55 scanf("%d", &n); 56 for(int i = 2; i <= n; i++) 57 { 58 int p; 59 scanf("%d", &p); 60 add(i, p), add(p, i); 61 } 62 dfs(1, 0); 63 LL ans = 0; 64 for(int i = 1; i <= n; i++) ans = (ans + dp[i][1]) % mod; 65 printf("%I64d\n", ans); 66 } 67 return 0; 68 }
HDU 5648 DZY Loves Math
预处理每个数的子集( 关于j = (j - 1) & i 这个飘逸写法我果然不是最后一个知道的- -。
枚举每对a = i & j, b = i | j,那么a就是i和j公用的bit,接下来任务就是把剩下的bit(即b - a)分配给i和j。
考虑把bit分给i,下界是b - m,小于这个值j就会大于m炸掉,上界是n - a,否则i爆掉。
然后在b - a的子集里面二分找上下界之间的个数就好了。
具体过程就是这样,但是要注意如果公共部分是0,两边至少都分配一个bit,因为i,j要正数。
1 #include <iostream> 2 #include <cstdio> 3 #include <vector> 4 #include <algorithm> 5 using namespace std; 6 typedef long long LL; 7 const int maxn = 1 << 16; 8 vector<int> A[maxn]; 9 int n, m, len; 10 LL ans; 11 12 int gcd(int a, int b) 13 { 14 if(!b) return a; 15 return a % b ? gcd(b, a % b) : b; 16 } 17 18 int get(int left, int x) 19 { 20 if(x < 0) return 0; 21 int l = 0, r = A[left].size() - 1, mid; 22 while(l < r) 23 { 24 mid = r - (r - l) / 2; 25 if(A[left][mid] <= x) l = mid; 26 else r = mid - 1; 27 } 28 return l + 1; 29 } 30 31 void dfs(int a, int b, int d) 32 { 33 if(d == len) 34 { 35 int left = b - a, l = left + a - m, r = n - a; 36 if(!a) r = min(r, left - 1), l = max(l, 1); 37 if(l > r) return; 38 // printf("a = %d , b = %d, gcd = %d, cnt = %d\n", a, b, gcd(a,b), get(left, r) - get(left, l - 1)); 39 ans += (LL) gcd(a, b) * (get(left, r) - get(left, l - 1)); 40 return; 41 } 42 dfs(a + (1 << d), b + (1 << d), d + 1); 43 dfs(a, b + (1 << d), d + 1); 44 dfs(a, b, d + 1); 45 } 46 47 int main(void) 48 { 49 for(int i = 0; i < maxn; i++) 50 { 51 for(int j = i; ; j = (j - 1) & i) 52 { 53 A[i].push_back(j); 54 if(!j) break; 55 } 56 reverse(A[i].begin(), A[i].end()); 57 } 58 int T; 59 scanf("%d", &T); 60 while(T--) 61 { 62 scanf("%d %d", &n, &m); 63 if(n < m) swap(n, m); 64 len = 0; 65 while((1 << len) <= n) len++; 66 ans = 0; 67 dfs(0, 0, 0); 68 printf("%I64d\n", ans); 69 } 70 return 0; 71 }
3.21
HDU 5649 DZY Loves Sorting
标算巧妙。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 const int maxn = 1e5 + 10; 6 int a[maxn], sum[maxn<<2], tag[maxn<<2]; 7 int M, op[maxn], L[maxn], R[maxn]; 8 9 void gather(int p) 10 { 11 sum[p] = sum[p<<1] + sum[p<<1|1]; 12 } 13 14 void push(int p, int m) 15 { 16 if(tag[p]) 17 { 18 tag[p<<1] = tag[p]; 19 tag[p<<1|1] = tag[p]; 20 sum[p<<1] = tag[p] == -1 ? 0 : (m - (m >> 1)); 21 sum[p<<1|1] = tag[p] == -1 ? 0 : (m >> 1); 22 tag[p] = 0; 23 } 24 } 25 26 void build(int p, int l, int r) 27 { 28 tag[p] = 0; 29 if(l < r) 30 { 31 int mid = (l + r) >> 1; 32 build(p<<1, l, mid); 33 build(p<<1|1, mid + 1, r); 34 gather(p); 35 } 36 else sum[p] = a[l] > M ? 1 : 0; 37 } 38 39 void modify(int p, int tl, int tr, int l, int r, int v) 40 { 41 if(tr < l || r < tl) return; 42 if(l <= tl && tr <= r) 43 { 44 tag[p] = v; 45 sum[p] = tag[p] == -1 ? 0 : (tr - tl + 1); 46 return; 47 } 48 push(p, tr - tl + 1); 49 int mid = (tl + tr) >> 1; 50 modify(p<<1, tl, mid, l, r, v); 51 modify(p<<1|1, mid+1, tr, l, r, v); 52 gather(p); 53 } 54 55 int query(int p, int tl, int tr, int l, int r) 56 { 57 if(tr < l || r < tl) return 0; 58 if(l <= tl && tr <= r) return sum[p]; 59 push(p, tr - tl + 1); 60 int mid = (tl + tr) >> 1; 61 int ret = query(p<<1, tl, mid, l, r); 62 ret += query(p<<1|1, mid+1, tr, l, r); 63 return ret; 64 } 65 66 int main(void) 67 { 68 int T; 69 scanf("%d", &T); 70 while(T--) 71 { 72 int n, m, k; 73 scanf("%d %d", &n, &m); 74 for(int i = 1; i <= n; i++) scanf("%d", a + i); 75 for(int i = 1; i <= m; i++) scanf("%d %d %d", op + i, L + i, R + i); 76 scanf("%d", &k); 77 int l = 1, r = n; 78 while(l < r) 79 { 80 M = l + (r - l) / 2; 81 build(1, 1, n); 82 for(int i = 1; i <= m; i++) 83 { 84 int tmp = query(1, 1, n, L[i], R[i]); 85 if(op[i]) modify(1, 1, n, L[i], L[i] + tmp - 1, 1), 86 modify(1, 1, n, L[i] + tmp, R[i], -1); 87 else modify(1, 1, n, L[i], R[i] - tmp, -1), 88 modify(1, 1, n, R[i] - tmp + 1, R[i], 1); 89 } 90 if(query(1, 1, n, k, k)) l = M + 1; 91 else r = M; 92 } 93 printf("%d\n", r); 94 } 95 return 0; 96 }
3.22
CF 653 D Delivery Bears
精度死QAQ
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <queue> 5 #include <algorithm> 6 using namespace std; 7 const int INF = 1e7; 8 const int maxn = 555; 9 const double eps = 1e-6; 10 int f[maxn], t[maxn], c[maxn]; 11 int lv[maxn], it[maxn]; 12 int cnt, h[maxn]; 13 14 struct edge 15 { 16 int to, pre, cap; 17 } e[maxn<<1]; 18 19 void init() 20 { 21 memset(h, -1, sizeof(h)); 22 cnt = 0; 23 } 24 25 void add(int from, int to, int cap) 26 { 27 e[cnt].pre = h[from]; 28 e[cnt].to = to; 29 e[cnt].cap = cap; 30 h[from] = cnt; 31 cnt++; 32 } 33 34 void ad(int from, int to, int cap) 35 { 36 add(from, to, cap); 37 add(to, from, 0); 38 } 39 40 void bfs(int s) 41 { 42 memset(lv, -1, sizeof(lv)); 43 queue<int> q; 44 lv[s] = 0; 45 q.push(s); 46 while(!q.empty()) 47 { 48 int v = q.front(); q.pop(); 49 for(int i = h[v]; i >= 0; i = e[i].pre) 50 { 51 int cap = e[i].cap, to = e[i].to; 52 if(cap > 0 && lv[to] < 0) 53 { 54 lv[to] = lv[v] + 1; 55 q.push(to); 56 } 57 } 58 } 59 } 60 61 int dfs(int v, int t, int f) 62 { 63 if(v == t) return f; 64 for(int &i = it[v]; i >= 0; i = e[i].pre) 65 { 66 int &cap = e[i].cap, to = e[i].to; 67 if(cap > 0 && lv[v] < lv[to]) 68 { 69 int d = dfs(to, t, min(f, cap)); 70 if(d > 0) 71 { 72 cap -= d; 73 e[i^1].cap += d; 74 return d; 75 } 76 } 77 } 78 return 0; 79 } 80 81 int Dinic(int s, int t) 82 { 83 int flow = 0; 84 while(1) 85 { 86 bfs(s); 87 if(lv[t] < 0) return flow; 88 memcpy(it, h, sizeof(it)); 89 int f; 90 while((f = dfs(s, t, INF)) > 0) flow += f; 91 } 92 } 93 94 int main(void) 95 { 96 int n, m, x; 97 scanf("%d %d %d", &n, &m, &x); 98 for(int i = 1; i <= m; i++) scanf("%d %d %d", f + i, t + i, c + i); 99 double l = 0, r = 1e6, mid; 100 while((r - l) * x > eps) 101 { 102 init(); 103 mid = (l + r) / 2; 104 for(int i = 1; i <= m; i++) ad(f[i], t[i], min(1.0 * x, c[i] / mid)); 105 if(Dinic(1, n) >= x) l = mid; 106 else r = mid; 107 } 108 printf("%.9f\n", l * x); 109 return 0; 110 }
3.23
什么都没干。
3.24
CF 653 G Move by Prime
由于对于每个质因子而言,它们对答案的贡献是独立的,所以分开考虑。
对于质因子p,n个数p的指数递增排序后为e1、e2……en。
考虑整个序列,怎么操作能使得move最少呢。
如果n为奇数,mid = e(n+1)/2,那么把所有的数都变为mid是最优的。
因为mid前面后面各有(n-1)/2个数,如果要把所有数转变为mid+1,
那么它前面的(n-1)/2个数都要多move一次,它后面的(n-1)/2数可以少move一次,
但是它自己也要多move一次,所以不如mid优,同理取mid-1也不如mid优。
类似的考虑n为偶数的情况,取mid为介于e(n/2)与e(n+2)/2之间的任意数均可。
那么把整个序列全部变为mid需要几步呢。
可以一对一对的考虑。
把en与e1都变成mid需要en - e1步,
把e(n-1)与e2都变成mid需要e(n-1) - e2步,
………………………………………………………………
所以总的move就是sigma(大于mid的数) - sigma(小于mid的数)
所以对于1~n的任意一个数,包含它的子序列一共有2^(n-1)个。
假设在a个序列中这个数处于序列的前半段,b个序列中这个数处于序列的后半段,c个序列中这个数处于序列的中间。
那么这个数仅考虑因子p对答案的贡献就是(b - a) * ek。
我们把(b - a)称为ek这个项的系数,仅考虑p的总答案就是sigma(系数k * ek)。
如何计算a和b呢。
可以考虑多项式(1 + x)^(k - 1) * (1 + x ^ (-1) )^(n - k)。
b就是正指数项的系数和,a就是负指数项系数和,c就是常数。
预处理组合数,然后就能计算每个数ek的系数。
由于ek的取值很有限(小于20),所以我们可以把系数求和,然后把e值相同的项一起求和。
总的复杂度就是把每个数分解的复杂度。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 typedef long long LL; 6 const LL mod = 1e9 + 7; 7 const int maxn = 3e5 + 1; 8 LL C[maxn], e[maxn], s[maxn]; 9 int t[maxn], cnt[maxn][20]; 10 11 // inv 12 LL qpow(LL a, int b) 13 { 14 LL ret = 1LL; 15 while(b) 16 { 17 if(b & 1) ret = ret * a % mod; 18 a = a * a % mod; 19 b >>= 1; 20 } 21 return ret; 22 } 23 24 LL inv(LL x) 25 { 26 return qpow(x, mod - 2); 27 } 28 29 int main(void) 30 { 31 int n; 32 scanf("%d", &n); 33 for(int i = 1; i <= n; i++) scanf("%d", t + i); 34 C[0] = 1LL; 35 for(int i = 1; i < n; i++) C[i] = C[i-1] * (n - i) % mod * inv(i) % mod; 36 for(int i = 0; i < n - 1; i++) e[1] = (e[1] - C[i] + mod) % mod; 37 for(int i = 2; i <= n; i++) e[i] = (e[i-1] + C[n-i] + C[n-i+1]) % mod; 38 for(int i = 1; i <= n; i++) s[i] = (s[i-1] + e[i]) % mod; 39 for(int i = 1; i <= n; i++) 40 { 41 for(int j = 2; j * j <= t[i]; j++) 42 { 43 int tmp = 0; 44 while(t[i] % j == 0) t[i] /= j, tmp++; 45 if(tmp) cnt[j][tmp]++; 46 } 47 if(t[i] > 1) cnt[t[i]][1]++; 48 } 49 LL ans = 0LL; 50 for(int i = 1; i < maxn; i++) 51 { 52 cnt[i][0] = n; 53 for(int j = 1; j < 20; j++) cnt[i][0] -= cnt[i][j]; 54 int cur = 0; 55 for(int j = 0; j < 20; j++) ans = (ans + (s[cur+cnt[i][j]] - s[cur] + mod) * j % mod) % mod, cur += cnt[i][j]; 56 } 57 printf("%I64d\n", ans); 58 return 0; 59 }
3.25
CF 653 E Bear and Forgotten Tree 2
先检查连通性,从2开始DFS一次,
set维护未访问点集,然后check禁边。
如果任一个联通块不与1相连则不可行。
考虑1的子树数目。
最小值为与1相连的联通块数目,最大值为n - 1 - 与1相连的禁边数。
只要k在这个范围内都可行。
1 #include <iostream> 2 #include <cstdio> 3 #include <vector> 4 #include <set> 5 #include <algorithm> 6 using namespace std; 7 const int maxn = 3e5 + 10; 8 vector<int> G[maxn]; 9 set<int> S; 10 11 bool dfs(int x) 12 { 13 vector<int> v; 14 bool ret = 0; 15 if(G[x].empty() || G[x][0] != 1) ret = 1; 16 for(set<int> :: iterator it = S.begin(); it != S.end(); it++) 17 { 18 vector<int> :: iterator vit = lower_bound(G[x].begin(), G[x].end(), *it); 19 if(vit != G[x].end() && *vit == *it) continue; 20 v.push_back(*it); 21 } 22 int sz = v.size(); 23 for(int i = 0; i < sz; i++) S.erase(v[i]); 24 for(int i = 0; i < sz; i++) ret |= dfs(v[i]); 25 return ret; 26 } 27 28 int main(void) 29 { 30 int n, m, k; 31 scanf("%d %d %d", &n, &m, &k); 32 for(int i = 0; i < m; i++) 33 { 34 int u, v; 35 scanf("%d %d", &u, &v); 36 G[u].push_back(v); 37 G[v].push_back(u); 38 } 39 for(int i = 2; i <= n; i++) S.insert(i); 40 for(int i = 1; i <= n; i++) sort(G[i].begin(), G[i].end()); 41 int ma = n - 1 - G[1].size(), mi = 0; 42 for(int i = 2; i <= n; i++) 43 { 44 if(S.find(i) == S.end()) continue; 45 S.erase(i); 46 if(!dfs(i)) return puts("impossible"); 47 mi++; 48 } 49 puts(k >= mi && k <= ma ? "possible" : "impossible"); 50 return 0; 51 }
3.26
CF 653 F Paper task
如果能算出每个后缀的前缀中有几个合法括号序列,
用SA排好,每个后缀的贡献就是这个后缀的前缀中的合法括号序列数,减去相邻后缀lcp的合法括号序列数。
于是考虑怎么搞后缀的前缀中有多少合法括号序列。
先预处理前缀和,左括号+1右括号-1。
把每个前缀和值对应的位置存起来,
然后对于某个后缀,只要找到它后面第一个前缀和小于它的位置,这两个位置中前缀和与它相等的位置都是合法括号序列。
反着用单调栈可以搞出每个后缀后面第一个前缀和小于它的位置。
1 #include <iostream> 2 #include <cstdio> 3 #include <vector> 4 #include <stack> 5 #include <cstring> 6 #include <algorithm> 7 using namespace std; 8 typedef long long LL; 9 const int maxn = 5e5 + 10; 10 int cnt[maxn], ed[maxn]; 11 vector<int> p[maxn<<1]; 12 vector<int> :: iterator it1, it2; 13 stack<int> st; 14 15 // SA 16 char s[maxn]; 17 int n, k, SA[maxn], r[maxn], tmp[maxn], h[maxn]; 18 bool cmp(int i, int j) 19 { 20 if(r[i] != r[j]) return r[i] < r[j]; 21 return ( i + k <= n ? r[i+k] : -1 ) < ( j + k <= n ? r[j+k] : -1 ); 22 } 23 24 void get_SA() 25 { 26 for(int i = 0; i <= n; i++) 27 { 28 SA[i] = i; 29 r[i] = i < n ? s[i] : -1; 30 } 31 for(k = 1; k <= n; k <<= 1) 32 { 33 sort(SA, SA + n + 1, cmp); 34 tmp[SA[0]] = 0; 35 for(int i = 1; i <= n; i++) tmp[SA[i]] = tmp[SA[i-1]] + cmp(SA[i-1], SA[i]); 36 memcpy(r, tmp, sizeof(r)); 37 } 38 return; 39 } 40 41 void get_height() 42 { 43 for(int i = 0; i <= n; i++) r[SA[i]] = i; 44 int k = 0; 45 for(int i = 0; i < n; i++) 46 { 47 if(k) k--; 48 int j = SA[r[i]-1]; 49 while(s[i+k] == s[j+k]) k++; 50 h[r[i]] = k; 51 } 52 return; 53 } 54 55 int main(void) 56 { 57 s[0] = '~'; 58 scanf("%d %s", &n, s + 1); 59 for(int i = 1; i <= n; i++) 60 { 61 cnt[i] = cnt[i-1] + (s[i] == '(' ? 1 : -1); 62 p[cnt[i]+maxn].push_back(i); 63 } 64 for(int i = n; i >= 0; i--) 65 { 66 while(!st.empty() && cnt[st.top()] >= cnt[i]) st.pop(); 67 ed[i] = st.empty() ? n : st.top() - 1; 68 st.push(i); 69 } 70 n++; 71 get_SA(); 72 get_height(); 73 LL ans = 0LL; 74 for(int i = 1; i < n; i++) 75 { 76 int q = SA[i] ? cnt[SA[i]-1] + maxn : maxn; 77 it1 = lower_bound(p[q].begin(), p[q].end(), ed[SA[i]-1]+1); 78 it2 = lower_bound(p[q].begin(), p[q].end(), SA[i] + h[i]); 79 if(it2 == p[q].end() || it2 > it1) continue; 80 ans += (LL) (it1 - it2); 81 } 82 printf("%I64d\n", ans); 83 return 0; 84 }
HDU 5653 Bomber Man wants to bomb an Array
比较挫是没有去想这样搞的复杂度。
区间长度n,炸弹m。
假设均分区间,区间长n/m,所以枚举每个炸弹以及左右端点只有m×n/m×n/m=n^2/m。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 double dp[2222]; 6 int b[2222]; 7 8 int main(void) 9 { 10 int T; 11 scanf("%d", &T); 12 while(T--) 13 { 14 int n, m; 15 scanf("%d %d", &n, &m); 16 for(int i = 0; i <= n; i++) dp[i] = 0.0; 17 for(int i = 1; i <= m; i++) scanf("%d", b + i), b[i]++; 18 b[m+1] = n + 1; 19 sort(b + 1, b + m + 1); 20 for(int i = 1; i <= m; i++) 21 for(int l = b[i-1] + 1; l <= b[i]; l++) 22 for(int r = b[i]; r < b[i+1]; r++) 23 dp[r] = max(dp[r], dp[l-1] + log2(r - l + 1)); 24 printf("%d\n", (int) floor(1e6 * dp[n])); 25 } 26 return 0; 27 }