2019DX#3
Solved | Pro.ID | Title | Ratio(Accepted / Submitted) |
1001 | Azshara's deep sea 凸包 | 6.67%(6/90) | |
👌 | 1002 | Blow up the city 反向边,支配树思想,u有多个父节点,直接连向他们的lca | 31.19%(126/404) |
1003 | Yukikaze and Demons 分治 | 16.67%(4/24) | |
👌 | 1004 | Distribution of books 二分+dp检查+数据结构优化 | 19.29%(93/482) |
1005 | Easy Math Problem Min25筛,杜教筛 | 9.30%(4/43) | |
![]() |
1006 | Fansblog | 21.17%(671/3170) |
![]() |
1007 | Find the answer | 11.24%(508/4521) |
ok | 1008 | Game 带修莫队 | 16.08%(23/143) |
👌 | 1009 | K Subsequence 优秀建图或者DJI版费用流,卡spfa | 5.81%(87/1497) |
1010 | Sindar's Art Exhibition 树链剖分 | 24.66%(18/73) | |
👌 | 1011 | Squrirrel 换根树形DP,注意细节 | 13.86%(46/332) |
1002 Blow up the city
支配树的思想,u有多个父节点,直接连向他们的lca
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <cstdlib> #include <iomanip> #include <bitset> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <stack> #include <cmath> #include <queue> #include <list> #include <map> #include <set> #include <cassert> // #include<bits/extc++.h> // using namespace __gnu_pbds; using namespace std; #define pb push_back #define fi first #define se second #define debug(x) cerr<<#x << " := " << x << endl; #define bug cerr<<"-----------------------"<<endl; #define FOR(a, b, c) for(int a = b; a <= c; ++ a) typedef long long ll; typedef long double ld; typedef pair<int, int> pii; typedef pair<ll, ll> pll; const int inf = 0x3f3f3f3f; const ll inff = 0x3f3f3f3f3f3f3f3f; const int mod = 998244353; /**********showtime************/ const int maxn = 1e5+9; vector<int>mp[maxn],g[maxn]; int du[maxn]; int dp[maxn]; int fa[maxn][20]; int lca(int x, int y){ if(dp[x] < dp[y]) swap(x, y); for(int i=19; i>=0; i--){ if(dp[y] <= dp[fa[x][i]]){ x = fa[x][i]; } } if(x == y) return x; for(int i=19; i>=0; i--){ if(fa[x][i] != fa[y][i]){ x = fa[x][i]; y = fa[y][i]; } } return fa[x][0]; } void bfs(int s) { queue<int>que; que.push(s); while(!que.empty()) { int u = que.front();que.pop(); for(int i=0; i<mp[u].size(); i++) { int v = mp[u][i]; du[v]--; if(du[v] == 0) { int p = g[v][0]; for(int j=1; j<g[v].size(); j++){ int a = g[v][j]; p = lca(p, a); } fa[v][0] = p; for(int i=1; i<20; i++) fa[v][i] = fa[fa[v][i-1]][i-1]; dp[v] = dp[p]+1; que.push(v); } } } } int main(){ int T; scanf("%d", &T); while(T--){ int n,m; scanf("%d%d", &n, &m); for(int i=0; i<=n; i++) mp[i].clear(), du[i] = 0,g[i].clear(),dp[i] = 0; for(int i=1; i<=m; i++) { int u,v; scanf("%d%d", &u, &v); mp[v].pb(u); g[u].pb(v); du[u]++; } int rt = 0; for(int i=1; i<=n; i++) { if(du[i]) continue; mp[rt].pb(i); g[i].pb(rt); du[i]++; } bfs(rt); int q; scanf("%d", &q); while(q--) { int a,b; scanf("%d%d", &a, &b); int l = lca(a, b); printf("%d\n", dp[a] + dp[b] - dp[l]); } } return 0; }
1004 Distribution of books
二分答案x
dp[]表示前i个在满足每段sum<=x的情况下最多分成几段。
转移dp[i] = max(dp[j] + 1), 要求sum[i] - sum[j] <= x
我是用线段树优化的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <cstdlib> #include <iomanip> #include <bitset> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <stack> #include <cmath> #include <queue> #include <list> #include <map> #include <set> #include <cassert> // #include<bits/extc++.h> // using namespace __gnu_pbds; using namespace std; #define pb push_back #define fi first #define se second #define debug(x) cerr<<#x << " := " << x << endl; #define bug cerr<<"-----------------------"<<endl; #define FOR(a, b, c) for(int a = b; a <= c; ++ a) typedef long long ll; typedef long double ld; typedef pair<int, int> pii; typedef pair<ll, ll> pll; const int inf = 0x3f3f3f3f; const ll inff = 0x3f3f3f3f3f3f3f3f; const int mod = 998244353; /**********showtime************/ const int maxn = 2e5+9; int a[maxn]; ll sum[maxn]; int n,m; vector<ll>v; int getid(ll x){ return lower_bound(v.begin(), v.end(), x) - v.begin() + 1; } int mx[(maxn * 2) << 2]; void build(int le, int ri, int rt) { if(le == ri) { mx[rt] = -inf; return; } int mid = (le + ri) >> 1; build(le, mid, rt<<1); build(mid+1, ri, rt<<1|1); mx[rt] = max(mx[rt<<1], mx[rt<<1|1]); } void update(int pos, int val, int le, int ri, int rt){ if(le == ri) { mx[rt] = max(mx[rt], val); return; } int mid = (le + ri) >> 1; if(pos <= mid) update(pos, val, le, mid, rt<<1); else update(pos, val, mid+1, ri, rt<<1|1); mx[rt] = max(mx[rt<<1], mx[rt<<1|1]); } int query(int L, int R, int le, int ri, int rt) { if(le >= L && ri <= R) { return mx[rt]; } int mid = (le + ri) >> 1; int res = -inf; if(mid >= L) res = max(res, query(L, R, le, mid, rt<<1)); if(mid < R) res = max(res, query(L, R, mid+1, ri, rt<<1|1)); return res; } bool check(ll ss) { v.clear(); v.pb(0); for(int i=1; i<=n; i++) {v.pb(sum[i]);v.pb(sum[i] - ss);} sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); int N = v.size(); build(1, N, 1); update(getid(0), 0, 1, N, 1); for(int i=1; i<=n; i++) { int le = getid(sum[i] - ss); int q = query(le, N, 1, N, 1) + 1; if(q >= m) return true; update(getid(sum[i]), q, 1, N, 1); } return false; } int main(){ int T; scanf("%d", &T); while(T--){ scanf("%d%d", &n, &m); ll le = 0, ri = 0; for(int i=1; i<=n; i++) { scanf("%d", &a[i]); sum[i] = sum[i-1] + a[i]; if(a[i] < 0) le += a[i]; ri = max(ri, 1ll*a[i]); } ll res; while(le <= ri) { ll mid = (le + ri) >> 1; if(check(mid)) ri = mid-1, res = mid; else le = mid+1; } printf("%lld\n", res); } return 0; } /* 1 5 4 1 1 1 -5 1 */
1008 Game
问题:是问区间$[L, R]$间,有多少子区间的异或和不为0.
我们求出前缀异或和,问题就转化为了区间中值相同的对数.
由于带修改,就转化为了带修改的莫队。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <cstdlib> #include <iomanip> #include <bitset> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <stack> #include <cmath> #include <queue> #include <list> #include <map> #include <set> #include <cassert> #include <unordered_map> // #include<bits/extc++.h> // using namespace __gnu_pbds; using namespace std; #define pb push_back #define fi first #define se second #define debug(x) cerr<<#x << " := " << x << endl; #define bug cerr<<"-----------------------"<<endl; #define FOR(a, b, c) for(int a = b; a <= c; ++ a) typedef long long ll; typedef unsigned long long ull; typedef long double ld; typedef pair<int, int> pii; typedef pair<ll, ll> pll; const int inf = 0x3f3f3f3f; const ll inff = 0x3f3f3f3f3f3f3f3f; const int mod = 998244353; template<typename T> inline T read(T&x){ x=0;int f=0;char ch=getchar(); while (ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x=f?-x:x; } /**********showtime************/ const int maxn = 1e5+9; int a[maxn], yuan[maxn]; int numsum[maxn]; struct Query{ int le, ri; int lb, rb; int tim; int id; } que[maxn]; struct Edit{ int pos; int preval, nxtval; } edit[maxn * 2]; bool cmp(Query a, Query b) { if(a.lb != b.lb) { return a.lb < b.lb; } if(a.rb != b.rb) { return a.rb < b.rb; } return a.tim < b.tim; } ll ans[maxn], pre[maxn]; const int N = 2e6+9; int cnt[N]; ll sum = 0; void add(int pos) { int val = numsum[pos]; sum -= pre[cnt[val]]; cnt[val] ++; sum += pre[cnt[val]]; } void del(int pos) { int val = numsum[pos]; sum -= pre[cnt[val]]; cnt[val]--; sum += pre[cnt[val]]; } ///时间增长 void up(int tt, int le, int ri) { int pos = edit[tt].pos; if(pos >= le && pos <= ri) { int val = numsum[pos]; sum -= pre[cnt[val]]; cnt[val] --; sum += pre[cnt[val]]; numsum[pos] = edit[tt].nxtval; val = numsum[pos]; sum -= pre[cnt[val]]; cnt[val]++; sum += pre[cnt[val]]; } else { numsum[pos] = edit[tt].nxtval; } } ///时间回退 void down(int tt, int le, int ri) { int pos = edit[tt].pos; if(pos >= le && pos <= ri) { int val = numsum[pos]; sum -= pre[cnt[val]]; cnt[val] --; sum += pre[cnt[val]]; numsum[pos] = edit[tt].preval; val = numsum[pos]; sum -= pre[cnt[val]]; cnt[val]++; sum += pre[cnt[val]]; } else { numsum[pos] = edit[tt].preval; } } int main(){ int n,m; for(int i=2; i<maxn; i++) { pre[i] = pre[i-1] + i - 1; } while(~scanf("%d%d", &n, &m)){ for(int i=1; i<=n; i++) scanf("%d", &a[i]), yuan[i] = a[i]; for(int i=1; i<=n; i++) numsum[i] = numsum[i-1] ^ a[i]; int qtot = 0, etot = 0; int block = 5000;//pow(n * m , 1.0/3)+1; for(int i=1; i<=m; i++) { int op; scanf("%d", &op); if(op == 1) { qtot++; scanf("%d%d", &que[qtot].le, &que[qtot].ri); que[qtot].le--; que[qtot].lb = que[qtot].le / block; que[qtot].rb = que[qtot].ri / block; que[qtot].tim = etot; que[qtot].id = qtot; } else { etot++; int pos; scanf("%d", &pos); edit[etot].pos = pos; edit[etot].preval = numsum[pos]; edit[etot].nxtval = numsum[pos + 1] ^ yuan[pos]; etot++; edit[etot].pos = pos + 1; edit[etot].preval = numsum[pos+1]; edit[etot].nxtval = numsum[pos+1]; numsum[pos] = numsum[pos + 1] ^ yuan[pos]; swap(yuan[pos], yuan[pos+1]); } } sort(que+1, que+1+qtot, cmp); for(int i=0; i<N; i++) cnt[i] = 0; for(int i=1; i<=n; i++) numsum[i] = numsum[i-1] ^ a[i]; int le = 0, ri = 0; int cur = 0; cnt[0] = 1; sum = 0; for(int i=1; i<=qtot; i++) { while(le < que[i].le) del(le), le++; while(le > que[i].le) le--, add(le); while(ri < que[i].ri) ri++, add(ri); while(ri > que[i].ri) del(ri), ri--; while(cur < que[i].tim) cur++,up(cur, le, ri); while(cur > que[i].tim) down(cur, le, ri), cur--; ans[que[i].id] = pre[que[i].ri - que[i].le + 1] - sum; } for(int i=1; i<=qtot; i++) printf("%lld\n", ans[i]); } return 0; }
1009 K Subsequence
卡了spfa,其他没了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <cstdlib> #include <iomanip> #include <bitset> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <stack> #include <cmath> #include <queue> #include <list> #include <map> #include <set> #include <cassert> using namespace std; const int inf = 0x3f3f3f3f; const int mod = 998244353; typedef long long ll; typedef pair<int,int>pii; #define pb push_back #define fi first #define se second /**********showtime************/ const int N = 4000 + 10; const int INF = 0x3f3f3f3f; struct edge { int to, cap, cost, rev; edge(int t, int c, int co,int re){ to = t;cap = c; cost = co; rev = re; } }; int V; vector<edge>g[N]; int h[N], dis[N], prevv[N], preve[N]; void addedge(int u, int v, int cap, int cost) { g[u].pb(edge(v, cap, cost, g[v].size())); g[v].pb(edge(u, 0, -cost, g[u].size()-1)); } int solve(int s, int t, int f) { int res = 0; memset(h, 0, sizeof(h)); while(f > 0) { priority_queue<pii, vector<pii>, greater<pii> > q; memset(dis, inf, sizeof(dis)); dis[s] = 0; q.push(pii(0, s)); while(!q.empty()) { pii p = q.top(); q.pop(); int v = p.se; if(dis[v] < p.fi) continue; for (int i = 0; i < g[v].size(); ++i) { edge &e = g[v][i]; if(e.cap > 0 && dis[e.to] > dis[v] + e.cost + h[v] - h[e.to]) { dis[e.to] = dis[v] + e.cost + h[v] - h[e.to]; prevv[e.to] = v; preve[e.to] = i; q.push(pii(dis[e.to], e.to)); } } } if(dis[t] == INF) return -1; for (int v = 0; v < V; ++v) h[v] += dis[v]; int d = f; for (int v = t; v != s; v = prevv[v]) d = min(d, g[prevv[v]][preve[v]].cap); f -= d; res += d*h[t]; for (int v = t; v != s; v = prevv[v]) { edge &e = g[prevv[v]][preve[v]]; e.cap -= d; g[v][e.rev].cap += d; } } return res; } const int maxn = 2e3+9; int a[maxn]; int main(){ int T; scanf("%d", &T); while(T--) { int n,k; scanf("%d%d", &n, &k); int s = 0, t = 2 * n + 1, ss = t+1; V = ss+1; for(int i=0; i<V; i++) g[i].clear(); for(int i=1; i<=n; i++) { scanf("%d", &a[i]); addedge(i, i+n, 1, -1*a[i]); } for(int i=1; i<=n; i++) { addedge(s, i, 1, 0); addedge(i + n, t, 1, 0); for(int j=i+1; j<=n; j++) { if(a[i] <= a[j] ) { addedge(i+n, j, inf, 0); } } } addedge(ss, s, k, 0); printf("%d\n", -1*solve(ss, t, k)); } return 0; }
1011 Squrirrel
换根的树形dp
由于有一条边可以删除,所以我们要把儿子分成三种(最长的儿子,次长的儿子,普通儿子),记录最长的儿子,次长的儿子,并预处理出换根时不同类型儿子要的数据,就是同一个父节点下最优的最长路长度。
这个题有一点要清楚,就是对于同一个父节点,只有把最长路儿子的一个路径消成0,才能减少这个父节点的答案。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <cstdlib> #include <iomanip> #include <bitset> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <stack> #include <cmath> #include <queue> #include <list> #include <map> #include <set> #include <cassert> // #include<bits/extc++.h> // using namespace __gnu_pbds; using namespace std; #define pb push_back #define fi first #define se second #define debug(x) cerr<<#x << " := " << x << endl; #define bug cerr<<"-----------------------"<<endl; #define FOR(a, b, c) for(int a = b; a <= c; ++ a) typedef long long ll; typedef long double ld; typedef pair<int, int> pii; typedef pair<ll, ll> pll; const int inf = 0x3f3f3f3f; const ll inff = 0x3f3f3f3f3f3f3f3f; const int mod = 998244353; /**********showtime************/ const int maxn = 2e5+9; vector<pii>mp[maxn]; int f[maxn][2], s[maxn][3]; int son[maxn][2]; void dfs1(int u, int fa) { f[u][0] =0; f[u][1] = 0; s[u][0] =0; s[u][1] = 0; s[u][2] = 0; int x = 0, y = 0; for(pii p : mp[u]) { int v = p.fi; if(v == fa) continue; dfs1(v, u); if(s[u][0] <= f[v][0] + p.se) { y = v; s[u][0] = f[v][0] + p.se; if(s[u][0] > f[u][0]) swap(s[u][0], f[u][0]), swap(x, y); } } son[u][0] = x; //最长儿子 son[u][1] = y; //次长儿子 if(y){ //最长儿子用 int m = 0; for(pii p : mp[u]) { int v = p.fi; if(v == fa) continue; if(v == x) continue; if(v != y){ m = max(m, f[v][0] + p.se); } else { m = max(m, min(f[v][1] + p.se, f[v][0])); } } s[u][2] = m; } if(x) { //普通儿子用 int m = 0; for(pii p : mp[u]) { int v = p.fi; if(v == fa) continue; if(v != x){ m = max(m, f[v][0] + p.se); } else { m = max(m, min(f[v][1] + p.se, f[v][0])); } } f[u][1] = m; } if(y) { //次长儿子用 int m = 0; for(pii p : mp[u]) { int v = p.fi; if(v == fa) continue; if(v == y) continue; if(v != x){ m = max(m, f[v][0] + p.se); } else m = max(m, min(f[v][1] + p.se, f[v][0])); } s[u][1] = m; } } int mx, ans; void dfs2(int u, int fa, int mx0, int mx1) { int x = max(mx0, f[u][1]); x = min(x, max(mx1, f[u][0])); if(mx > x) mx = x, ans = u; else if(mx == x) ans = min(ans, u); for(pii p : mp[u]) { int v = p.fi; if(v == fa) continue; int tmpmx0 = mx0; int tmpmx1 = mx1; if(son[u][0] == v) { tmpmx0 = max(mx0, s[u][0]); } else tmpmx0 = max(mx0, f[u][0]); if(son[u][0] == v){ tmpmx1 = min(max(mx0, s[u][2]), max(mx1, s[u][0]));//x去掉和去掉次大一条边 //注意这里或的关系,只能有一个裁剪 } else { if(son[u][1] != v) { tmpmx1 = min(max(mx1, f[u][0]),max(mx0, f[u][1])); } else { tmpmx1 = min(max(mx0, s[u][1]) , max(mx1, f[u][0])); } } tmpmx1 = min(tmpmx0, tmpmx1 + p.se); tmpmx0 += p.se; dfs2(v, u, tmpmx0, tmpmx1); } } int main(){ int T; scanf("%d", &T); while(T--) { int n; scanf("%d", &n); for(int i=1; i<=n; i++) mp[i].clear(); for(int i=1; i<n; i++) { int u,v,w; scanf("%d%d%d", &u, &v, &w); mp[u].pb(pii(v, w)); mp[v].pb(pii(u, w)); } dfs1(1, 1); mx = inf, ans = inf; dfs2(1, 1, 0, 0); printf("%d %d\n", ans, mx); } return 0; } /* 1 10 2 1 139 3 1 72 4 2 141 5 2 133 6 5 121 7 5 49 8 7 182 9 5 174 10 9 171 output:5 344 */
skr