2018 Multi-University Training Contest 5
贱贱补不动题了T^T
发现之前的点双都在乱写,就顺便花了点时间重造板子
考虑一个仙人掌,割要么是一条树边要么是环上两条边,注意到切环时其中一条一定是环上最小的
跑一遍Tarjan搞出所有点双,把环上最小边切了权值加到环上其他边上,这样转化为树上问题
树上做法,类似于Kruskal从大到小合并边,两边的子树大小乘积为这条边的贡献
由于这里的贡献带了异或,按位考虑即可,注意答案暴了ll
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef unsigned long long ull; 4 const int maxn = 1e5 + 10; 5 const int maxm = 2e5 + 10; 6 int u[maxm], v[maxm], w[maxm]; 7 vector<int> G[maxn], E; 8 bool cmp(int i, int j) { 9 return w[i] > w[j]; 10 } 11 12 // BCC 13 stack<int> S; 14 vector<int> bcc[maxm]; 15 int bcc_cnt, bccno[maxm]; 16 int dfs_clock, dfn[maxn], low[maxn], iscut[maxn]; 17 void dfs(int x, int fe) { // fe 表示父边 id 18 low[x] = dfn[x] = ++dfs_clock; 19 int ch = 0; 20 for (int i = 0; i < G[x].size(); ++i) { 21 int e = G[x][i]; 22 if (e == fe) continue; 23 int y = u[e] == x ? v[e] : u[e]; 24 if (!dfn[y]) { 25 S.push(e); 26 ch++, dfs(y, e); // 注意是 e 27 low[x] = min(low[x], low[y]); 28 if (low[y] >= dfn[x]) { 29 iscut[x] = 1; 30 bcc_cnt++; 31 while (1) { 32 int t = S.top(); 33 S.pop(); 34 bccno[t] = bcc_cnt; 35 bcc[bcc_cnt].push_back(t); 36 if (t == e) break; 37 } 38 } 39 } else if (dfn[y] < dfn[x]) { 40 low[x] = min(low[x], dfn[y]); 41 S.push(e); 42 } 43 } 44 if (!fe && ch == 1) iscut[x] = 0; 45 } 46 void find_bcc(int n, int m) { 47 dfs_clock = bcc_cnt = 0; 48 for (int i = 0; i <= n; ++i) dfn[i] = iscut[i] = 0; 49 for (int i = 0; i <= m; ++i) bccno[i] = 0, bcc[i].clear(); 50 for (int i = 1; i <= n; ++i) if (!dfn[i]) dfs(i, 0); 51 } 52 53 int fa[maxn], r[maxn][31][2]; 54 void init(int n) { 55 for(int i = 0; i <= n; ++i) { 56 fa[i] = i; 57 for(int j = 0; j <= 30; ++j) { 58 r[i][j][1] = (i & (1 << j)) ? 1 : 0; 59 r[i][j][0] = 1 - r[i][j][1]; 60 } 61 } 62 } 63 int Find(int x) { 64 return fa[x] == x ? x : fa[x] = Find(fa[x]); 65 } 66 void Union(int x, int y) { 67 x = Find(x), y = Find(y); 68 if(x == y) return; 69 fa[x] = y; 70 for(int j = 0; j <= 30; ++j) 71 for(int k = 0; k <= 1; ++k) r[y][j][k] += r[x][j][k]; 72 } 73 74 int main() { 75 int T; 76 scanf("%d", &T); 77 while(T--) { 78 int n, m; 79 scanf("%d %d", &n, &m); 80 for(int i = 1; i <= n; ++i) G[i].clear(); 81 for(int i = 1; i <= m; ++i) { 82 scanf("%d %d %d", u + i, v + i, w + i); 83 G[u[i]].push_back(i); 84 G[v[i]].push_back(i); 85 } 86 find_bcc(n, m); 87 E.clear(); 88 for(int i = 1; i <= bcc_cnt; ++i) { 89 if(bcc[i].size() == 1) { 90 E.push_back(bcc[i][0]); 91 continue; 92 } 93 int mi = 1000000000, p; 94 for(int j = 0; j < bcc[i].size(); ++j) { 95 int x = bcc[i][j]; 96 if(w[x] < mi) mi = w[x], p = j; 97 } 98 for(int j = 0; j < bcc[i].size(); ++j) { 99 if(j == p) continue; 100 int x = bcc[i][j]; 101 E.push_back(x), w[x] += mi; 102 } 103 } 104 init(n); 105 ull ans = 0; 106 sort(E.begin(), E.end(), cmp); 107 for(int i = 0; i < E.size(); ++i) { 108 int x = E[i], U = Find(u[x]), V = Find(v[x]), W = w[x]; 109 for(int j = 0; j <= 30; ++j) { 110 if(W & (1 << j)) ans += ((ull) r[U][j][0] * r[V][j][0] + (ull) r[U][j][1] * r[V][j][1]) * (1 << j); 111 else ans += ((ull) r[U][j][0] * r[V][j][1] + (ull) r[U][j][1] * r[V][j][0]) * (1 << j); 112 } 113 Union(U, V); 114 } 115 printf("%llu\n", ans); 116 } 117 return 0; 118 }
暴力全排列竟然过了
1 #include <bits/stdc++.h> 2 using namespace std; 3 vector< vector<int> > P[11]; 4 vector<int> num[11]; 5 6 int main() { 7 int fac = 1; 8 for(int i = 1; i <= 9; ++i) { 9 fac *= i; 10 vector<int> v; 11 for(int j = 1; j <= i; ++j) v.push_back(j); 12 for(int j = 0; j < fac; ++j) { 13 int vis[11] = {0}, cnt = 0; 14 for(int k = 1; k <= i; ++k) { 15 if(vis[k]) continue; 16 int p = k; 17 while(!vis[p]) vis[p] = 1, p = v[p - 1], cnt++; 18 cnt--; 19 } 20 P[i].push_back(v); 21 num[i].push_back(cnt); 22 next_permutation(v.begin(), v.end()); 23 } 24 } 25 int T; 26 scanf("%d", &T); 27 while(T--) { 28 int n, k; 29 scanf("%d %d", &n, &k); 30 if(n == 1000000000) { 31 puts("1000000000 1000000000"); 32 continue; 33 } 34 int mi = n, ma = n, b = 0, d[11] = {0}; 35 while(n) d[++b] = n % 10, n /= 10; 36 for(int i = 0; i < P[b].size(); ++i) { 37 if(num[b][i] > k) continue; 38 if(b > 1 && d[P[b][i][b - 1]] == 0) continue; 39 int x = 0; 40 for(int j = b - 1; j >= 0; --j) x = x * 10 + d[P[b][i][j]]; 41 mi = min(x, mi), ma = max(x, ma); 42 } 43 printf("%d %d\n", mi, ma); 44 } 45 return 0; 46 }
传说中的Easy题
分圆多项式有$(1 - x^n) = \prod_{d|n}\Phi_{d}(x)$,莫比乌斯反演一下$\Phi_{n}(x)=\prod_{d|n}(1 - x^{\frac{n}{d}})^{\mu(d)}$
题面说了$\Phi_{n}(x)$是$\varphi(n)$次多项式,而一个数最多只有$6$个不同的素因子,考虑莫比乌斯函数不为$0$的项,求单个$\Phi_{n}(x)$最多做$2^6$次多项式乘除法
而每次乘除的都是一个只有两项的多项式,可以直接在模$x^{\varphi(n) + 1}$下算,用类似背包的写法,单次就是$\varphi(n)$的复杂度
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef pair<int, int> pii; 4 const int maxn = 1e5 + 10; 5 6 int tot, check[maxn], prime[maxn], phi[maxn], mu[maxn]; 7 void Sieve() { 8 tot = 0; 9 phi[1] = mu[1] = 1; 10 memset(check, 0, sizeof(check)); 11 for(int i = 2; i < maxn; ++i) { 12 if(!check[i]) prime[++tot] = i, phi[i] = i - 1, mu[i] = -1; 13 for(int j = 1; j <= tot; ++j) { 14 if(i * prime[j] >= maxn) break; 15 check[i * prime[j]] = 1; 16 if(i % prime[j] == 0) { 17 phi[i * prime[j]] = phi[i] * prime[j]; 18 mu[i * prime[j]] = 0; 19 break; 20 } 21 else { 22 phi[i * prime[j]] = phi[i] * (prime[j] - 1); 23 mu[i * prime[j]] = -mu[i]; 24 } 25 } 26 } 27 } 28 29 vector<int> fac[maxn], Mul[maxn], Div[maxn]; 30 vector< vector<pii> > v; 31 vector<int> id; 32 bool cmp(int i, int j) { 33 for(int k = 0; ; ++k) { 34 if(k == v[i].size()) return v[j][k].second > 0; 35 if(k == v[j].size()) return v[i][k].second < 0; 36 if(v[i][k].first > v[j][k].first) return v[i][k].second < 0; 37 if(v[i][k].first < v[j][k].first) return v[j][k].second > 0; 38 if(v[i][k].second != v[j][k].second) return v[i][k].second < v[j][k].second; 39 } 40 } 41 void solve(int n) { 42 vector<int> p(phi[n] + 1, 0); 43 p[0] = 1; 44 for(int i = 0; i < Mul[n].size(); ++i) { 45 int k = Mul[n][i]; 46 for(int j = phi[n]; j - k >= 0; --j) p[j] -= p[j - k]; 47 } 48 for(int i = 0; i < Div[n].size(); ++i) { 49 int k = Div[n][i]; 50 for(int j = 0; j + k <= phi[n]; ++j) p[j + k] += p[j]; 51 } 52 vector<pii> t; 53 for(int i = phi[n]; i >= 0; --i) { 54 if(p[i] == 0) continue; 55 t.push_back(pii(i, p[i])); 56 } 57 v.push_back(t); 58 } 59 60 void p(vector<pii> &t) { 61 stringstream s; 62 s << "("; 63 int flag = 0; 64 for(int k = 0; k < t.size(); ++k) { 65 int x = t[k].first, y = t[k].second; 66 if(y < 0) s << "-"; 67 else if(flag) s << "+"; 68 if(abs(y) != 1) s << abs(y); 69 if(x > 0) s << "x"; 70 else if(abs(y) == 1) s << "1"; 71 if(x > 1) s << "^" << x; 72 flag = 1; 73 } 74 s << ")"; 75 cout << s.str(); 76 } 77 78 int main() { 79 Sieve(); 80 for(int i = 1; i <= 100000; ++i) { 81 for(int j = i; j <= 100000; j += i) fac[j].push_back(i); 82 if(!mu[i]) continue; 83 for(int j = i; j <= 100000; j += i) { 84 if(mu[i] == 1) Mul[j].push_back(j / i); 85 else Div[j].push_back(j / i); 86 } 87 } 88 int T; 89 scanf("%d", &T); 90 while(T--) { 91 int n; 92 scanf("%d", &n); 93 v.clear(), id.clear(); 94 for(int i = 0; i < fac[n].size(); ++i) if(fac[n][i] > 1) solve(fac[n][i]); 95 for(int i = 0; i < v.size(); ++i) id.push_back(i); 96 sort(id.begin(), id.end(), cmp); 97 printf("(x-1)"); 98 for(int i = 0; i < v.size(); ++i) p(v[id[i]]); 99 puts(""); 100 } 101 return 0; 102 }
答案为到$u$不超过$w$的点加到$v$不超过$w$的点减去到$u$,$v$都不超过的点,即减去的部分是到$u$,$v$中点不超过$w - \frac{dis(u, v)}{2}$的点
注意到中点可能不是一个节点而是某一条边的中点,即问题可归结为两种询问,询问距离点$u$不超过$k$的点数和询问距离边$u-v$不超过$k$的点数
对于第一个经典问题,首先树分治并维护点分树,每个重心用一个不定长数组记录子树中到重心距离为$k$的点数,同时记录到点分树父亲距离为$k$的点数
对于每次询问,从询问点往点分树根部爬,每次计算该重心子树中的贡献,并减去会与父亲重复计算的部分
对于第二个问题,注意到$u$与$v$是相邻点,故在点分树上必然一个点是另一个点的祖先,那么从点分树上深度较大的点出发会经过所有包含$u$或者$v$的子树
对于询问,只要在每个重心时将询问点取$u$与$v$中距离重心较近的一个即可
卡常有意思吗加了一堆inline过了
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 // fastIO 5 namespace fastIO { 6 #define BUF_SIZE 100000 7 //fread -> read 8 bool IOerror = 0; 9 inline char nc() { 10 static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE; 11 if(p1 == pend) { 12 p1 = buf; 13 pend = buf + fread(buf, 1, BUF_SIZE, stdin); 14 if(pend == p1) { 15 IOerror = 1; 16 return -1; 17 } 18 } 19 return *p1++; 20 } 21 inline bool blank(char ch) { 22 return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; 23 } 24 inline void read(int &x) { 25 char ch; 26 while(blank(ch = nc())); 27 if(IOerror) 28 return; 29 for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0'); 30 } 31 #undef BUF_SIZE 32 }; 33 using namespace fastIO; 34 35 36 const int maxn = 1e5 + 10; 37 vector<int> G[maxn]; 38 39 // LCA 40 int dfs_clock; 41 int dep[maxn], tid[maxn << 1], dfn[maxn]; 42 int fa[maxn][21]; 43 // 进出时间都要记录,数组需要开两倍 44 void dfs(int x, int f) { 45 fa[x][0] = f; 46 dep[x] = dep[f] + 1; 47 tid[++dfs_clock] = x, dfn[x] = dfs_clock; 48 for (int i = 0; i < G[x].size(); ++i) { 49 int to = G[x][i]; 50 if (to == f) continue; 51 dfs(to, x); 52 tid[++dfs_clock] = x; // 每个点出栈时加入父亲 53 } 54 } 55 int lg2[maxn << 1], rmq[maxn << 1][21]; 56 void init(int n) { 57 // LCA-ST 58 lg2[0] = -1; 59 for (int i = 1; i <= dfs_clock; ++i) lg2[i] = lg2[i >> 1] + 1; 60 for (int i = 1; i <= dfs_clock; ++i) rmq[i][0] = tid[i]; 61 for (int j = 1; (1 << j) <= dfs_clock; ++j) { 62 for (int i = 1; i + (1 << j) - 1 <= dfs_clock; ++i) { 63 int ls = rmq[i][j - 1], rs = rmq[i + (1 << (j - 1))][j - 1]; 64 rmq[i][j] = dep[ls] < dep[rs] ? ls : rs; 65 } 66 } 67 // 树上倍增 68 for (int j = 1; (1 << j) <= n; ++j) 69 for (int i = 1; i <= n; ++i) fa[i][j] = fa[fa[i][j - 1]][j - 1]; 70 } 71 inline int LCA(int x, int y) { 72 if (dfn[x] > dfn[y]) swap(x, y); 73 int k = lg2[dfn[y] - dfn[x] + 1]; 74 int ls = rmq[dfn[x]][k], rs = rmq[dfn[y] - (1 << k) + 1][k]; 75 return dep[ls] < dep[rs] ? ls : rs; 76 } 77 inline int dis(int x, int y) { 78 int lca = LCA(x, y); 79 return dep[x] + dep[y] - dep[lca] * 2; 80 } 81 82 struct V { 83 vector<int> c; 84 V(int n = 0) {vector<int>(n + 1, 0).swap(c);} 85 inline void modify(int i, int x) { 86 if (i >= 0 && i < c.size()) c[i] += x; 87 } 88 inline void sum() { 89 for (int i = 1; i < c.size(); ++i) c[i] += c[i - 1]; 90 } 91 inline int query(int i) { 92 if (i >= 0) return c[min((int) c.size() - 1, i)]; 93 return 0; 94 } 95 } T[maxn], FT[maxn]; 96 97 // D&C 98 int vis[maxn], sz[maxn]; 99 void SZ(int x, int f) { 100 sz[x] = 1; 101 for (int i = 0; i < G[x].size(); ++i) { 102 int to = G[x][i]; 103 if (vis[to] || to == f) continue; 104 SZ(to, x), sz[x] += sz[to]; 105 } 106 } 107 int tot, mi, rt; 108 // 重心:最大子树最小 109 void RT(int x, int f) { 110 int ma = tot - sz[x]; 111 for (int i = 0; i < G[x].size(); ++i) { 112 int to = G[x][i]; 113 if (vis[to] || to == f) continue; 114 RT(to, x), ma = max(ma, sz[to]); 115 } 116 if (ma < mi) mi = ma, rt = x; 117 } 118 // 处理点分树上父亲至子树中的点最近距离 119 int F[maxn], D[maxn], mf[maxn]; 120 void MF(int x, int f, int C) { 121 mf[C] = min(mf[C], dis(F[C], x)); 122 for (int i = 0; i < G[x].size(); ++i) { 123 int to = G[x][i]; 124 if (vis[to] || to == f) continue; 125 MF(to, x, C); 126 } 127 } 128 void INS(int x, int f, int C) { 129 T[C].modify(dis(x, C), 1); 130 if (F[C]) FT[C].modify(dis(x, F[C]) - mf[C], 1); 131 for (int i = 0; i < G[x].size(); ++i) { 132 int to = G[x][i]; 133 if (vis[to] || to == f) continue; 134 INS(to, x, C); 135 } 136 } 137 void build(int x, int f) { 138 SZ(x, 0); 139 tot = mi = sz[x], RT(x, 0), x = rt; 140 F[x] = f, D[x] = D[f] + 1; 141 T[x] = V(tot); 142 // FT[x].query(i) 询问的是子树中距离父亲 i + mf[x] 的点权值和 143 if (f) FT[x] = V(tot), mf[x] = dis(x, f), MF(x, 0, x); 144 INS(x, 0, x), T[x].sum(), FT[x].sum(); 145 vis[x] = 1; 146 for (int i = 0; i < G[x].size(); ++i) { 147 int to = G[x][i]; 148 if (vis[to]) continue; 149 build(to, x); 150 } 151 } 152 int q1(int x, int y, int k) { 153 int ret = T[x].query(k - dis(x, y)); 154 int fd = dis(F[x], y); 155 // 询问一直处理到根,即便中间有点贡献为零 156 if (F[x]) ret += q1(F[x], y, k) - FT[x].query(k - fd - mf[x]); 157 return ret; 158 } 159 int q2(int x, int u, int v, int k) { 160 int ret = T[x].query(k - min(dis(x, u), dis(x, v))); 161 int fd = min(dis(F[x], u), dis(F[x], v)); 162 if (F[x]) ret += q2(F[x], u, v, k) - FT[x].query(k - fd - mf[x]); 163 return ret; 164 } 165 166 int anc(int x, int d) { 167 for(int i = 20; i >= 0; --i) 168 if(d >= (1 << i)) d -= (1 << i), x = fa[x][i]; 169 return x; 170 } 171 int m1(int u, int v) { 172 int d = dis(u, v); 173 int lca = LCA(u, v); 174 if(dep[u] - dep[lca] > dep[v] - dep[lca]) return anc(u, d / 2); 175 return anc(v, d / 2); 176 } 177 typedef pair<int, int> pii; 178 pii m2(int u, int v) { 179 int lca = LCA(u, v); 180 int d = dis(u, v), dl = d / 2, dr = d - dl; 181 int ml = dep[u] - dep[lca] >= dl ? anc(u, dl) : anc(v, dr); 182 int mr = dep[u] - dep[lca] >= dr ? anc(u, dr) : anc(v, dl); 183 return pii(ml, mr); 184 } 185 186 int main() { 187 int T; 188 read(T); 189 while(T--) { 190 int n, m; 191 read(n), read(m); 192 for(int i = 1; i <= n; ++i) G[i].clear(), vis[i] = 0; 193 for(int i = 2; i <= n; ++i) { 194 int u, v; 195 read(u), read(v); 196 G[u].push_back(v); 197 G[v].push_back(u); 198 } 199 dfs_clock = 0, dfs(1, 0), init(n); 200 build(1, 0); 201 int ans = 0; 202 while(m--) { 203 int u, v, w; 204 read(u), read(v), read(w); 205 u = (u + ans) % n + 1; 206 v = (v + ans) % n + 1; 207 w = (w + ans) % n; 208 int d = dis(u, v); 209 ans = q1(u, u, w) + q1(v, v, w); 210 if(d <= w + w) { 211 if(d & 1) { 212 pii t = m2(u, v); 213 u = t.first, v = t.second; 214 ans -= q2(D[u] > D[v] ? u : v, u, v, w - d / 2 - 1); 215 } 216 else { 217 int mid = m1(u, v); 218 ans -= q1(mid, mid, w - d / 2); 219 } 220 } 221 printf("%d\n", ans); 222 } 223 } 224 return 0; 225 }
为什么不用判断优弧劣弧呢呢
1 #include <bits/stdc++.h> 2 using namespace std; 3 const double pi = acos(-1); 4 int sqr(int x) {return x * x;} 5 6 int main() { 7 int T; 8 scanf("%d", &T); 9 while(T--) { 10 int m, R; 11 scanf("%d %d", &m, &R); 12 double ans = 2 * R * pi; 13 while(m--) { 14 int x, y, r; 15 scanf("%d %d %d", &x, &y, &r); 16 if(sqr(x) + sqr(y) >= sqr(R + r)) continue; 17 if(sqr(x) + sqr(y) < sqr(R - r)) continue; 18 double d = sqrt(sqr(x) + sqr(y)); 19 double theta1 = 2 * acos((sqr(R) + sqr(x) + sqr(y) - sqr(r)) / (2 * R * d)); 20 double theta2 = 2 * acos((sqr(r) + sqr(x) + sqr(y) - sqr(R)) / (2 * r * d)); 21 ans -= theta1 * R - theta2 * r; 22 } 23 printf("%.8f\n", ans); 24 } 25 return 0; 26 }
前面一堆著名结论我是不知道的,直接从转变为$\sum_{i = 1}^{n}x_{i} = \lfloor \frac{1}{2}\sum_{i = 1}^{n}(p_{i} + 1) \rfloor$开始做
首先容斥成$\sum_{I \subseteq J} (-1) ^{|I|} C_{M - 1 - \sum_{i \in I} p_{i}}^{n - 1}$,组合数是关于$\sum_{i \in I} p_{i}$的$n - 1$次多项式
可以先暴力把组合数拆开,求出多项式的系数,然后分别考虑每一项的贡献,这样就可以折半枚举一边,再用二项式定理合起来
广义组合数是有负数定义的,但是考虑实际情况组合数不为零需要满足$M - \sum_{i \in I} p_{i} \geq n$
所以折半的两边要先按大小排序,枚举一半的时候用双指针维护另一半的幂的和
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const ll mod = 1e9 + 7; 5 6 ll fp(ll a, ll b) { 7 ll ret = 1; 8 while (b) { 9 if (b & 1) ret = ret * a % mod; 10 a = a * a % mod; 11 b >>= 1; 12 } 13 return ret; 14 } 15 ll inv(ll x) { 16 return fp(x, mod - 2); 17 } 18 19 ll sum[2][1 << 17]; 20 int id[2][1 << 17], cnt[2][1 << 17]; 21 bool cmp1(int i, int j) { 22 return sum[0][i] < sum[0][j]; 23 } 24 bool cmp2(int i, int j) { 25 return sum[1][i] > sum[1][j]; 26 } 27 28 int main() { 29 ll c[44][44] = {0}; 30 for(int i = 0; i < 44; ++i) c[i][0] = c[i][i] = 1; 31 for(int i = 2; i < 44; ++i) 32 for(int j = 1; j < i; ++j) 33 c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod; 34 int T; 35 scanf("%d", &T); 36 while(T--) { 37 int n; 38 scanf("%d", &n); 39 ll M = 0, p[35], a[35] = {1}, t = 1; 40 for(int i = 1; i <= n; ++i) scanf("%lld", p + i), M += p[i] + 1; 41 M /= 2; 42 for(int i = 0; i < n - 1; ++i) { 43 t = t * (i + 1) % mod; 44 for(int j = i + 1; j > 0; --j) a[j] = mod - a[j - 1]; 45 a[0] = 0; 46 for(int j = 0; j <= i; ++j) a[j] = (a[j] + a[j + 1] * (mod + 1 - M % mod + i)) % mod; 47 } 48 t = inv(t); 49 for(int i = 0; i <= n - 1; ++i) a[i] = a[i] * t % mod; 50 int l = n / 2, r = n - l; 51 for(int i = 0; i < (1 << l); ++i) { 52 id[0][i] = i, sum[0][i] = cnt[0][i] = 0; 53 for(int j = 0; j < l; ++j) 54 if(i & (1 << j)) sum[0][i] = sum[0][i] + p[j + 1], cnt[0][i]++; 55 } 56 sort(id[0], id[0] + (1 << l), cmp1); 57 for(int i = 0; i < (1 << r); ++i) { 58 id[1][i] = i, sum[1][i] = cnt[1][i] = 0; 59 for(int j = 0; j < r; ++j) 60 if(i & (1 << j)) sum[1][i] = sum[1][i] + p[l + j + 1], cnt[1][i]++; 61 } 62 sort(id[1], id[1] + (1 << r), cmp2); 63 int pos = 0; 64 ll ans = 0, s[2][44] = {0}; 65 for(int i = 0; i < (1 << r); ++i) { 66 int x = id[1][i]; 67 if(sum[1][x] > M - n) continue; 68 while(pos < (1 << l) && sum[0][id[0][pos]] + sum[1][x] <= M - n) { 69 int y = id[0][pos]; 70 ll tmp = 1; 71 for(int j = 0; j < n; ++j) { 72 s[cnt[0][y] % 2][j] = (s[cnt[0][y] % 2][j] + tmp) % mod; 73 tmp = tmp * (sum[0][y] % mod) % mod; 74 } 75 pos++; 76 } 77 for(int j = 0; j < n; ++j) { 78 ll tmp = 0, mul = 1; 79 for(int k = 0; k <= j; ++k) { 80 tmp = (tmp + mul * c[j][k] % mod * s[cnt[1][x] % 2][j - k]) % mod; 81 tmp = (tmp + mod - mul * c[j][k] % mod * s[(cnt[1][x] % 2) ^ 1][j - k] % mod) % mod; 82 mul = mul * (sum[1][x] % mod) % mod; 83 } 84 ans = (ans + tmp * a[j]) % mod; 85 } 86 } 87 printf("%lld\n", ans); 88 } 89 return 0; 90 }
反着用ST表
考虑正常ST表,首先预处理的时候每个长度为$2^k$的区间的$\max$是两个重叠的长度为$2^{k-1}$的子区间传递过来的,询问的时候只要找两个长度不大于$log_{2}(r - l + 1)$的区间合并
反过来就是每次修改对两个长度不大于$log_{2}(r - l + 1)$的区间标记,后处理的时候把每个长度为$2^k$的区间的标记下传到两个重叠的长度为$2^{k-1}$的子区间
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef unsigned int ui; 4 5 ui X, Y, Z; 6 ui f() { 7 X = X ^ (X << 11); 8 X = X ^ (X >> 4); 9 X = X ^ (X << 5); 10 X = X ^ (X >> 14); 11 ui W = X ^ (Y ^ Z); 12 X = Y; 13 Y = Z; 14 Z = W; 15 } 16 17 const int maxn = 1e5 + 10; 18 int rmq[maxn][20], lg[maxn]; 19 void init(int n) { 20 for (int j = 0; j <= lg[n]; j++) 21 for (int i = 1; i + (1 << j) - 1 <= n; i++) 22 rmq[i][j] = 0; 23 } 24 void modify(int l, int r, int v) { 25 int k = lg[r - l + 1]; 26 rmq[l][k] = max(rmq[l][k], v); 27 rmq[r - (1 << k) + 1][k] = max(rmq[r - (1 << k) + 1][k], v); 28 } 29 void cal(int n) { 30 for (int j = lg[n]; j >= 1; j--) 31 for (int i = 1; i + (1 << j) - 1 <= n; i++) 32 rmq[i][j - 1] = max(rmq[i][j - 1], rmq[i][j]), rmq[i + (1 << j - 1)][j - 1] = max( 33 rmq[i + (1 << j - 1)][j - 1], rmq[i][j]); 34 } 35 36 int main() { 37 lg[0] = -1; 38 for(int i = 1; i < maxn; ++i) lg[i] = lg[i / 2] + 1; 39 int T; 40 scanf("%d", &T); 41 for(int i = 1; i <= T; ++i) { 42 int n, m; 43 scanf("%d %d %u %u %u", &n, &m, &X, &Y, &Z); 44 init(n); 45 for(int i = 1; i <= m; ++i) { 46 int l = f() % n + 1, r = f() % n + 1, v = f() % (1 << 30); 47 if(l > r) swap(l, r); 48 modify(l, r, v); 49 } 50 cal(n); 51 long long ans = 0; 52 for(int i = 1; i <= n; ++i) ans ^= 1ll * i * rmq[i][0]; 53 printf("%lld\n", ans); 54 } 55 return 0; 56 }
$f[i][j]$表示前缀$[1...i]$最后一个数是$j$的最长不减子序列,$g[i][j][k][l]$表示前缀$[1...i]$第一段(递增)末尾是数字$j$,第二段(递减)开头是数字$k$,末尾是数字$l$的最长子序列
注意到需要满足$j \leq l \leq k$所以实际状态没有那么多,第一维滚动数组优化掉,同时从$f$转移到$g$的时候记录一下左端点,第三段(递增)和第一段一样后缀预处理好
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn = 1e5 + 10; 4 int f[maxn][10], g[10][10][10], h[10][10][10], lg[10][10][10], lh[10][10][10]; 5 int p[maxn][10]; 6 int A[maxn]; 7 char s[maxn]; 8 9 int main() { 10 int T; 11 scanf("%d", &T); 12 while(T--) { 13 int n; 14 scanf("%d %s", &n, s + 1); 15 for(int i = 1; i <= n; ++i) A[i] = s[i] - '0'; 16 for(int i = 0; i <= n + 1; ++i) 17 for(int j = 0; j < 10; ++j) f[i][j] = p[i][j] = 0; 18 for(int i = n; i >= 1; --i) { 19 for(int j = 0; j < 10; ++j) p[i][j] = p[i + 1][j]; 20 for(int j = 9; j >= A[i]; --j) p[i][A[i]] = max(p[i][A[i]], p[i + 1][j] + 1); 21 } 22 for(int j = 0; j < 10; ++j) for(int k = 9; k >= j; --k) for(int l = j; l <= k; ++l) g[j][k][l] = lg[j][k][l] = 0; 23 int m = 0, L = 0, R = 0; 24 for(int i = 1; i <= n; ++i) { 25 for(int j = 0; j < 10; ++j) f[i][j] = f[i - 1][j]; 26 for(int j = 0; j <= A[i]; ++j) f[i][A[i]] = max(f[i][A[i]], f[i - 1][j] + 1); 27 for(int j = 0; j < 10; ++j) for(int k = 9; k >= j; --k) for(int l = j; l <= k; ++l) 28 h[j][k][l] = g[j][k][l], lh[j][k][l] = lg[j][k][l]; 29 for(int j = 0; j <= A[i]; ++j) if(f[i - 1][j] + 1 > g[j][A[i]][A[i]]) g[j][A[i]][A[i]] = f[i - 1][j] + 1, lg[j][A[i]][A[i]] = i; 30 for(int j = 0; j <= A[i]; ++j) for(int k = 9; k >= A[i]; --k) for(int l = k; l >= A[i]; --l) 31 if(h[j][k][l] + 1 > g[j][k][A[i]]) g[j][k][A[i]] = h[j][k][l] + 1, lg[j][k][A[i]] = lh[j][k][l]; 32 for(int j = 0; j <= A[i]; ++j) for(int k = 9; k >= A[i]; --k) for(int l = 9; l >= k; --l) 33 if(g[j][k][A[i]] + p[i + 1][l] > m) m = g[j][k][A[i]] + p[i + 1][l], L = lg[j][k][A[i]], R = i; 34 } 35 if(R && !L) L = 1; 36 if(!L && !R) int x = 1 / 0; 37 printf("%d %d %d\n", m, L, R); 38 } 39 return 0; 40 }
题解有点没说清楚阿……
考虑没有下界约束的情况,这个做法很多,但是为了后面方便,先考虑一个暴力数位打牌的做法
先把问题拆分成$log$个,从高位往低位,找到第一个位$b$满足$N$个数中至少有一个此位与上界$R$不相等
如果不存在这样的$b$,那么这$N$个数都与上界相等,这个情况单独考虑即可
找到$b$后,要$dp$出所有满足至少一个数此位小于上界的方案数,状态为$f[i][j][k]$表示取了$i$个数,$j = 0/1$表示前$i$个数中是否有此位小于上界,$k$表示前$i$个数此位异或值
转移的时候枚举新加的一个数,第$b$位是$0/1$,若此位填的数与上界相等,那么后缀填数方案要满足不大于上界,否则后面可以填任意数
注意到每增加一个数的转移是一样的,可以对第一维做矩阵快速幂转移
再考虑有下界的情况,可以暴力容斥转化为没有下界的情况,即至少$0$个数小于下界$-$至少$1$个数小于下界$+$至少$2$个数下于下界……
也就是说新加入一个数的时候,要考虑这个数的约束是仅小于上界,还是同时小于下界两种情况转移,状态也要多加一维$f[i][j][k][l]$其中$l$表示$i$个数中强制要求小于下界的数有$l$个
这样状态数就炸了,但是很容易发现容斥的时候正负号只和$l$的奇偶有关,所以状态存$l \ mod \ 2$就可以了,依然可以矩阵快速幂把$i$优化掉
询问时对于每个$K$,枚举一下$b$统计方案,并单独处理$b$不存在的情况
两个容易忽视的坑点是$K$大于最高位要直接输出$0$,$R$为$0$要特判……
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const ll mod = 1e9 + 7; 5 // f[i][j][k] 是否卡上界 当前0/1 <L数目%2 6 7 const int maxm = 8; 8 struct Mat { 9 ll a[maxm][maxm]; 10 Mat() {memset(a, 0, sizeof(a));} 11 void E() { 12 for(int i = 0; i < maxm; ++i) 13 for(int j = 0; j < maxm; ++j) 14 a[i][j] = i == j ? 1 : 0; 15 } 16 } f[30]; 17 void Mul(Mat A, Mat B, Mat& C) { 18 for(int i = 0; i < maxm; ++i) 19 for(int j = 0; j < maxm; ++j) 20 C.a[i][j] = 0; 21 for(int i = 0; i < maxm; ++i) { 22 for(int j = 0; j < maxm; ++j) { 23 if(!A.a[i][j]) continue; 24 for(int k = 0; k < maxm; ++k) { 25 if(!B.a[j][k]) continue; 26 C.a[i][k] = (C.a[i][k] + A.a[i][j] * B.a[j][k]) % mod; 27 } 28 } 29 } 30 } 31 void Pow(Mat& A, ll n) { 32 Mat ret; ret.E(); 33 while(n) { 34 if(n & 1) Mul(ret, A, ret); 35 Mul(A, A, A), n >>= 1; 36 } 37 A = ret; 38 } 39 40 int main() { 41 int T; 42 scanf("%d", &T); 43 while(T--) { 44 int N, L, R, Q; 45 scanf("%d %d %d %d", &N, &L, &R, &Q); 46 int mx = 0; 47 while((1 << mx) <= R) ++mx; 48 for(int b = 0; b < mx; ++b) { 49 f[b] = Mat(); 50 int rb = ((1 << b) & R) ? 1 : 0, lb = L && ((1 << b) & (L - 1)) ? 1 : 0; 51 for(int i = 0; i <= 1; ++i) for(int j = 0; j <= 1; ++j) for(int k = 0; k <= 1; ++k) { 52 int msk = (i << 2) + (j << 1) + k; 53 for(int nb = 0; nb <= 1; ++nb) { 54 if(rb || !nb) { 55 int ni = i && (rb == nb), nj = j ^ nb, nk = k; 56 int nmsk = (ni << 2) + (nj << 1) + nk; 57 f[b].a[nmsk][msk] += nb < rb ? (i ? 1 : (1 << b)) : ((R & ((1 << b) - 1)) + 1); 58 } 59 if(L && (lb || !nb)) { 60 int ni = i && (lb == nb), nj = j ^ nb, nk = k ^ 1; 61 int nmsk = (ni << 2) + (nj << 1) + nk; 62 f[b].a[nmsk][msk] += nb < lb ? (i ? 1 : (1 << b)) : (((L - 1) & ((1 << b) - 1)) + 1); 63 } 64 } 65 } 66 Pow(f[b], N); 67 } 68 while(Q--) { 69 int K; 70 scanf("%d", &K); 71 if(K >= (1 << mx)) {puts("0"); continue;} 72 if(R == 0) {puts("1"); continue;} 73 ll ans = 0; 74 int o = N & 1, r = o ? R : 0, l = L ? r ^ R ^ (L - 1) : 0; 75 int okr = 1, okl = 1; 76 for(int b = mx - 1; b >= 0; --b) { 77 int nr = (r & (1 << b)) ? 1 : 0; 78 int nl = (l & (1 << b)) ? 1 : 0; 79 int nk = (K & (1 << b)) ? 1 : 0; 80 if(okr) ans = (ans + f[b].a[nk << 1][4]) % mod; 81 if(L && okl) ans = (ans + mod - f[b].a[(nk << 1) + 1][4]) % mod; 82 if(nr != nk) okr = 0; 83 if(nl != nk) okl = 0; 84 } 85 if(r == K) ans = (ans + f[0].a[4 + ((K & 1) << 1)][4]) % mod; 86 if(L && l == K) ans = (ans + mod - f[0].a[4 + ((K & 1) << 1) + 1][4]) % mod; 87 printf("%lld\n", ans); 88 } 89 } 90 return 0; 91 }
题意有点迷,一次$Conjecture$本质上是询问目标串是否在一个子集中,这个子集是可以任意选取的
考虑一个简单版本,一个多重集,等概率选择一个数,按同样的规则每次猜测该数是否在某子集中,合理决策使询问期望最少
一次询问根据回答的$Yes/No$实际上是将一个集合划分为两部分,即询问的子集和它的补集,至上而下划分便形成了一棵二叉决策树
那么使询问期望最小,等同于让每个点的深度与概率乘积之和最小,本质上是求一个哈夫曼树,也就是经典的合并果子问题
于是原问题可以看成两部分,第一部分是统计出现$i$次的子串数目,然后就转化为上面的简化问题,需要做一个哈夫曼树
第一部分可牛逼了,首先需要一个代表国内最牛逼后缀数组水平的$SA-IS$,这个可以去陈敏锐小给给的博客那蒯一个
然后建一个小根的笛卡尔树,就可以线性统计出现$i$次的子串数目,然后好像笛卡尔树这里深搜爆栈了很难受只好换了个丑丑的$BFS$
统计完后开始合并果子,由于一共有$\frac{n(n + 1)}{2}$个子串,所以出现次数相同的要一起合并
对于某个频数的子串,如果有偶数个就直接自己合并,否则留一个出来和后面比它大的合并
对于频数小于$n$的子串,从小到大边扫边合并就行了,发现会合并出大小为$O(n^2)$级别的节点出来
但是由于总串数的限制,大于$n$的结点数是$O(n)$级别的,所以一旦合并出来大于$n$,就丢到一个队列里,最后做一次普通的合并果子即可
btw感觉dls这题代码合并的时候是$O(nlogn)$的……
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 5 const int N = 1e6 + 10; 6 namespace SA { 7 int sa[N], rk[N], ht[N], s[N<<1], t[N<<1], p[N], cnt[N], cur[N]; 8 #define pushS(x) sa[cur[s[x]]--] = x 9 #define pushL(x) sa[cur[s[x]]++] = x 10 #define inducedSort(v) fill_n(sa, n, -1); fill_n(cnt, m, 0); \ 11 for (int i = 0; i < n; i++) cnt[s[i]]++; \ 12 for (int i = 1; i < m; i++) cnt[i] += cnt[i-1]; \ 13 for (int i = 0; i < m; i++) cur[i] = cnt[i]-1; \ 14 for (int i = n1-1; ~i; i--) pushS(v[i]); \ 15 for (int i = 1; i < m; i++) cur[i] = cnt[i-1]; \ 16 for (int i = 0; i < n; i++) if (sa[i] > 0 && t[sa[i]-1]) pushL(sa[i]-1); \ 17 for (int i = 0; i < m; i++) cur[i] = cnt[i]-1; \ 18 for (int i = n-1; ~i; i--) if (sa[i] > 0 && !t[sa[i]-1]) pushS(sa[i]-1) 19 void sais(int n, int m, int *s, int *t, int *p) { 20 int n1 = t[n-1] = 0, ch = rk[0] = -1, *s1 = s+n; 21 for (int i = n-2; ~i; i--) t[i] = s[i] == s[i+1] ? t[i+1] : s[i] > s[i+1]; 22 for (int i = 1; i < n; i++) rk[i] = t[i-1] && !t[i] ? (p[n1] = i, n1++) : -1; 23 inducedSort(p); 24 for (int i = 0, x, y; i < n; i++) if (~(x = rk[sa[i]])) { 25 if (ch < 1 || p[x+1] - p[x] != p[y+1] - p[y]) ch++; 26 else for (int j = p[x], k = p[y]; j <= p[x+1]; j++, k++) 27 if ((s[j]<<1|t[j]) != (s[k]<<1|t[k])) {ch++; break;} 28 s1[y = x] = ch; 29 } 30 if (ch+1 < n1) sais(n1, ch+1, s1, t+n, p+n1); 31 else for (int i = 0; i < n1; i++) sa[s1[i]] = i; 32 for (int i = 0; i < n1; i++) s1[i] = p[sa[i]]; 33 inducedSort(s1); 34 } 35 template<typename T> 36 int mapCharToInt(int n, const T *str) { 37 int m = *max_element(str, str+n); 38 fill_n(rk, m+1, 0); 39 for (int i = 0; i < n; i++) rk[str[i]] = 1; 40 for (int i = 0; i < m; i++) rk[i+1] += rk[i]; 41 for (int i = 0; i < n; i++) s[i] = rk[str[i]] - 1; 42 return rk[m]; 43 } 44 // Ensure that str[n] is the unique lexicographically smallest character in str. 45 template<typename T> 46 void suffixArray(int n, const T *str) { 47 int m = mapCharToInt(++n, str); 48 sais(n, m, s, t, p); 49 for (int i = 0; i < n; i++) rk[sa[i]] = i; 50 for (int i = 0, h = ht[0] = 0; i < n-1; i++) { 51 int j = sa[rk[i]-1]; 52 while (i+h < n && j+h < n && s[i+h] == s[j+h]) h++; 53 if (ht[rk[i]] = h) h--; 54 } 55 } 56 }; 57 58 int a[N], *h; 59 int fa[N], st[N], l[N], r[N], vis[N]; 60 ll tot, f[N], sz[N]; 61 void dfs(int x, int fa) { 62 int len = h[x]; 63 if(fa) len -= h[fa]; 64 sz[x] = 2; 65 if(l[x]) dfs(l[x], x), sz[x] += sz[l[x]] - 1; 66 if(r[x]) dfs(r[x], x), sz[x] += sz[r[x]] - 1; 67 f[sz[x]] += len, tot -= sz[x] * len; 68 } 69 70 vector<int> v; 71 queue<int> q; 72 queue<ll> q1, q2; 73 int main() { 74 int T; 75 scanf("%d", &T); 76 while(T--) { 77 ll n; 78 scanf("%lld", &n); 79 for(int i = 0; i < n; ++i) scanf("%d", a + i), a[i]++; 80 a[n] = 0; 81 SA :: suffixArray(n, a); 82 h = SA :: ht + 1; 83 int top = 0; 84 for (int i = 1; i < n; ++i) l[i] = r[i] = vis[i] = 0; 85 for (int i = 1; i < n; ++i) { 86 int k = top; 87 while (k > 0 && h[st[k - 1]] > h[i]) --k; 88 if (k) r[st[k - 1]] = i; 89 if (k < top) l[i] = st[k]; 90 st[k++] = i; 91 top = k; 92 } 93 for (int i = 1; i < n; ++i) vis[l[i]] = vis[r[i]] = 1; 94 int rt = 0; 95 for (int i = 1; i < n; ++i) if (!vis[i]) rt = i; 96 for (int i = 1; i <= n; ++i) f[i] = 0; 97 tot = n * (n + 1) / 2; 98 while(!q.empty()) q.pop(); 99 q.push(rt); 100 fa[rt] = 0; 101 v.clear(); 102 while(!q.empty()) { 103 int x = q.front(); q.pop(); 104 v.push_back(x); 105 if(l[x]) q.push(l[x]), fa[l[x]] = x; 106 if(r[x]) q.push(r[x]), fa[r[x]] = x; 107 } 108 for(int i = v.size() - 1; i >= 0; --i) { 109 int x = v[i], len = h[x]; 110 if(fa[x]) len -= h[fa[x]]; 111 sz[x] = 2; 112 if(l[x]) sz[x] += sz[l[x]] - 1; 113 if(r[x]) sz[x] += sz[r[x]] - 1; 114 f[sz[x]] += len, tot -= sz[x] * len; 115 } 116 f[1] = tot; 117 ll ans = 0, o = 0; 118 for(int i = 1; i <= n; ++i) { 119 if(!f[i]) continue; 120 if(o) { 121 f[i]--; 122 if(i + o <= n) f[i + o]++; 123 else q1.push(i + o); 124 ans += i + o, o = 0; 125 } 126 if(f[i] & 1) o = i, f[i]--; 127 if(!f[i]) continue; 128 if(i + i <= n) f[i + i] += f[i] / 2, ans += f[i] * i; 129 else for(int j = 1; j <= f[i] / 2; ++j) q1.push(i + i), ans += i + i; 130 } 131 while(!q1.empty() || !q2.empty()) { 132 ll x = 0, y = 0; 133 if(o) x = o, o = 0; 134 else { 135 if(!q1.empty() && !q2.empty()) { 136 if(q1.front() < q2.front()) x = q1.front(), q1.pop(); 137 else x = q2.front(), q2.pop(); 138 } 139 else if(!q1.empty()) x = q1.front(), q1.pop(); 140 else x = q2.front(), q2.pop(); 141 } 142 if(q1.empty() && q2.empty()) break; 143 if(!q1.empty() && !q2.empty()) { 144 if(q1.front() < q2.front()) y = q1.front(), q1.pop(); 145 else y = q2.front(), q2.pop(); 146 } 147 else if(!q1.empty()) y = q1.front(), q1.pop(); 148 else y = q2.front(), q2.pop(); 149 ans += x + y; 150 q2.push(x + y); 151 } 152 ll t = n * (n + 1) / 2; 153 ll g = __gcd(ans, t); 154 ans /= g, t /= g; 155 printf("%lld", ans); 156 if(t > 1) printf("/%lld", t); 157 puts(""); 158 } 159 return 0; 160 }
好像不会计数哇T^T
论文都没有的题不补><