考虑一个暴力:维护一个点集 S,初始为空。依次考虑点 u,如果询问 S∪{u} 的结果是 ∣S∣+1,说明 u 到 S 中没有边,将 u 加入 S。遍历所有点后,对于所有不在 S 中的点(设这些点构成点集 T),可以在 S 内部二分找到其所有连向 S 内部的边。最后,我们确定 S 内部没有边,并找到了所有 S,T 之间的边,然后直接对 T 递归做。
inlineintfindEdge(const vector <int> &t, int bl, int s){ vector <int> tmp; for (int i = bl;i < t.size();i++) tmp.push_back(t[i]); tmp.push_back(s); if (Query(tmp) == tmp.size()) return-1; int l = bl, r = t.size() - 1; while (l < r) { int mid = l + r >> 1; vector <int> tmp; for (int i = l;i <= mid;i++) tmp.push_back(t[i]); tmp.push_back(s); if (Query(tmp) != tmp.size()) r = mid; else l = mid + 1; } return l; }
inlinevoidbuildGraph(const vector <int> &v){ if (v.size() <= 1) return; vector <int> iso, nxt; for (int x : v) { iso.push_back(x); if (Query(iso) < iso.size()) { iso.pop_back(); nxt.push_back(x); } } for (int x : nxt) { int p = 0, l; while ((l = findEdge(iso, p, x)) != -1) { g[x].push_back(iso[l]); g[iso[l]].push_back(x); p = l + 1; } } buildGraph(nxt); }
inlinevoidWork(){ vector <int> bg; for (int i = 1;i <= 2 * n;i++) bg.push_back(i); buildGraph(bg); for (int i = 1;i <= 2 * n;i++) { if (g[i].size() == 3) { for (int a = 0;a < g[i].size();a++) { for (int b = a + 1;b < g[i].size();b++) { vector <int> tmp; tmp.push_back(g[i][a]); tmp.push_back(i); tmp.push_back(g[i][b]); if (Query(tmp) == 1) { suki[g[i][0] + g[i][1] + g[i][2] - g[i][a] - g[i][b]][i] = 1; suki[i][g[i][0] + g[i][1] + g[i][2] - g[i][a] - g[i][b]] = 1; goto done; } } } } done:; } for (int i = 1;i <= 2 * n;i++) { if (!vis[i]) { for (int v : g[i]) { if (!suki[i][v] && !suki[v][i]) { Answer(v, i); vis[i] = vis[v] = 1; break; } } } } } }
inlinelonglongSolve(int nda, int sgl, int sgr, int flg, int ndb){ if (sgl > sgr) { sgl = sgr = -1; flg = 0; } longlong res = -1; if (flg == 0) { if (nda == ndb) { longlong ans = 0; if (als[nda]) ans += Solve(als[nda], -1, -1, 0, bls[ndb]); if (ars[nda]) ans += Solve(ars[nda], -1, -1, 0, brs[ndb]); res = ans % mod; } elseif (nda < ndb) { int u = nda; longlong ans = 0; while (u < ndb) { u = ars[u]; if (als[u]) ans += dpa[als[u]][0] + art[als[u]] - alt[als[u]] + 1; } int l = nda, r = u; ans += ndb - nda; if (als[nda]) ans += Solve(als[nda], l, ndb - 1, 2, bls[ndb]); else ans += dpb[bls[ndb]][0]; if (ars[u]) ans += Solve(ars[u], ndb + 1, r, 1, brs[ndb]); else ans += dpb[brs[ndb]][1]; res = ans % mod; } elseif (nda > ndb) { int u = nda; longlong ans = 0; while (u > ndb) { u = als[u]; if (ars[u]) ans += dpa[ars[u]][1] + art[ars[u]] - alt[ars[u]] + 1; } int l = u, r = nda; ans += nda - ndb; if (als[u]) ans += Solve(als[u], l, ndb - 1, 2, bls[ndb]); else ans += dpb[bls[ndb]][0]; if (ars[nda]) ans += Solve(ars[nda], ndb + 1, r, 1, brs[ndb]); else ans += dpb[brs[ndb]][1]; res = ans % mod; } } elseif (flg == 1) { if (sgl <= ndb && ndb <= sgr) res = (ndb - sgl + dpb[bls[ndb]][0] + Solve(nda, ndb + 1, sgr, 1, brs[ndb])) % mod; else { int u = nda; longlong ans = 0; while (u < ndb) { if (als[u]) ans += dpa[als[u]][0] + art[als[u]] - alt[als[u]] + 1; u = ars[u]; } if (als[u]) ans += dpa[als[u]][0] + art[als[u]] - alt[als[u]] + 1; u = ars[u]; if (u == 0) res = (ans + dpb[ndb][1]) % mod; else res = (ans + Solve(u, sgl, fa[u], 1, ndb)) % mod; } } else { if (sgl <= ndb && ndb <= sgr) res = (sgr - ndb + dpb[brs[ndb]][1] + Solve(nda, sgl, ndb - 1, 2, bls[ndb])) % mod; else { int u = nda; longlong ans = 0; while (u > ndb) { if (ars[u]) ans += dpa[ars[u]][1] + art[ars[u]] - alt[ars[u]] + 1; u = als[u]; } if (ars[u]) ans += dpa[ars[u]][1] + art[ars[u]] - alt[ars[u]] + 1; u = als[u]; if (u == 0) res = (ans + dpb[ndb][0]) % mod; else res = (ans + Solve(u, fa[u], sgr, 2, ndb)) % mod; } } return (res % mod + mod) % mod; }
P9096 [PA2020] Sen o podboju
数据随机自然是乱搞。
考虑一个暴力 dp:fi,j,k 表示 i 子树,断了 j 条边,根所在连通块的 a 的和是 k,最小的连通块平方和。
structEdge { int to, nxt; Edge() { nxt = -1; } }; int n, hd[N], siz[N], pnt; longlong a[N]; Edge e[N << 1]; vector <pair <longlong, longlong> > dp[N][N], tmp[N];
inlinevoidAddEdge(int u, int v){ e[++pnt].to = v; e[pnt].nxt = hd[u]; hd[u] = pnt; }
inlinevoidRead(){ n = qread(); for (int i = 1;i <= n;i++) a[i] = qread(); for (int i = 1;i < n;i++) { int u = qread(), v = qread(); AddEdge(u, v); AddEdge(v, u); } }
inlinevoidDfs(int u, int fa){ siz[u] = 1; for (int i = 0;i <= n;i++) dp[u][i].clear(); dp[u][0].push_back(make_pair(a[u], a[u] * a[u])); for (int i = hd[u];~i;i = e[i].nxt) { int v = e[i].to; if (v != fa) { Dfs(v, u); for (int i = 0;i <= siz[v];i++) { for (int j = 0;j <= siz[u];j++) { for (auto x : dp[v][i]) { for (auto y : dp[u][j]) { tmp[i + j].push_back(make_pair(x.first + y.first, x.second + y.second + 2 * x.first * y.first)); tmp[i + j + 1].push_back(make_pair(y.first, x.second + y.second)); } } } } siz[u] += siz[v]; for (int i = 0;i <= siz[u];i++) { dp[u][i].clear(); sort(tmp[i].begin(), tmp[i].end()); longlong mnv = 0x3f3f3f3f3f3f3f3f; for (int j = 0;j < tmp[i].size();j++) { if (tmp[i][j].second < mnv) { mnv = tmp[i][j].second; dp[u][i].push_back(tmp[i][j]); } } tmp[i].clear(); } } } }
P9108 [PA2020] Malowanie płotu
考虑 dp,设 fi,j 表示前 i 列,强制第 i 列第 j 行被涂上,以及第 i 列第 j+1∼m 行没有被涂上的方案数。(即强制涂上的区间的下端点是 j)
注意到这等于前 i 列,强制第 i 列第 m−j+1 行被涂上,以及第 i 列第 1∼m−j 行没有被涂上的方案数。(即强制涂上的区间的上端点是 m−j+1)
则从第 i−1 列向第 i 列转移时,考虑强制第 i−1 列涂上的区间的上端点是 k。我们要求 k≤j。
intmain(){ n = qread(); for (int i = 1;i <= n;i++) { int r = qread(), w = qread(), t = qread(); cnt[t - w + 1000005][r - 1]++; } int ans = 0; for (int i = 1;i <= 3000000;i++) ans += min(cnt[i][0], cnt[i][1]); cout << ans << endl; return0; }