第四周 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 }
Aguin

 

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 }
Aguin

 

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 }
Aguin

 

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 }
Aguin

 

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 }
Aguin

 

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 }
Aguin

 

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 }
Aguin

 

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 }
Aguin

 

posted @ 2016-03-21 22:46  Aguin  阅读(173)  评论(1编辑  收藏  举报