《Educational Codeforces Round 112 (Rated for Div. 2)》
这场前面几题感觉都不是很难。
A:很显然很大的时候都用最大的来塞就行了,对于较小的时候用背包计算下最优的方案。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<LL,int> pii; const int N = 1e6 + 1; const int M = 1e5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; LL dp[N]; void init() { memset(dp,0x3f3f3f3f,sizeof(dp)); dp[0] = 0; dp[1] = dp[2] = dp[3] = dp[4] = dp[5] = dp[6] = 15; dp[7] = 20,dp[8] = 20,dp[9] = 25,dp[10] = 25; for(int i = 11;i < N;++i) { dp[i] = min(dp[i],dp[i - 6] + 15); dp[i] = min(dp[i],dp[i - 8] + 20); dp[i] = min(dp[i],dp[i - 10] + 25); } } int main() { init(); int ca;scanf("%d",&ca); int up = 1e6; LL ma = M * 25; while(ca--) { LL n;scanf("%lld",&n); if(n > up) { LL x = n / up; LL y = n - x * up; LL ans = ma * x + dp[y]; printf("%lld\n",ans); } else printf("%lld\n",dp[n]); } //system("pause"); return 0; }
B:显然四个角落代表四种最优情况。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<LL,int> pii; const int N = 1e6 + 1; const int M = 1e5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; int main() { int ca;scanf("%d",&ca); while(ca--) { int W,H;scanf("%d %d",&W,&H); int x1,y1,x2,y2;scanf("%d %d %d %d",&x1,&y1,&x2,&y2); int w,h;scanf("%d %d",&w,&h); int w1 = x2 - x1,h1 = y2 - y1; int lenw = w1 + w,lenh = h1 + h; if(lenw > W && lenh > H) printf("-1\n"); else { int dis1 = 0; if(x1 < w) dis1 = w - x1; if(lenw > W) dis1 = INF; int dis2 = 0; if(x2 > W - w) dis2 = x2 - (W - w); if(lenw > W) dis2 = INF; int dis3 = 0; if(y1 < h) dis3 = h - y1; if(lenh > H) dis3 = INF; int dis4 = 0; if(y2 > H - h) dis4 = y2 - (H - h); if(lenh > H) dis4 = INF; // printf("d1 is %d d2 is %d d3 is %d d4 is %d\n",dis1,dis2,dis3,dis4); double ans = min(1.0 * dis1,1.0 * dis2); ans = min(ans,1.0 * dis3); ans = min(ans,1.0 * dis4); printf("%.8f\n",ans); } } // system("pause"); return 0; }
C:感觉比A都简单。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<LL,int> pii; const int N = 1e5 + 1; const int M = 1e5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e18 #define dbg(ax) cout << "now this num is " << ax << endl; int a[5][N]; LL sum[5][N]; int main() { int ca;scanf("%d",&ca); while(ca--) { memset(sum,0,sizeof(sum)); int m;scanf("%d",&m); for(int i = 1;i <= 2;++i) for(int j = 1;j <= m;++j) scanf("%d",&a[i][j]),sum[i][j] = sum[i][j - 1] + a[i][j]; LL ans = INF; for(int i = 1;i <= m;++i) { LL ma1 = sum[1][m] - sum[1][i]; LL ma2 = sum[2][i - 1]; ans = min(ans,max(ma1,ma2)); } printf("%lld\n",ans); } //system("pause"); return 0; }
D:分析题意可以发现,满足条件的字符串只有abc三种交叉着来。
那么我们可以发现,如果开头的两个字符固定,那么后面的所有字符也就固定了。
也就是说字符串只有ab,ac,ba,bc,ca,cb六种开头的形式,前缀和一下,取最小的即可。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<LL,int> pii; const int N = 2e5 + 5; const int M = 1e5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; int n,m; string s; LL pre[10][N]; char cal(char a,char b) { if(a == 'a' && b == 'b' || a == 'b' && b == 'a') return 'c'; if(a == 'a' && b == 'c' || a == 'c' && b == 'a') return 'b'; if(a == 'b' && b == 'c' || a == 'c' && b == 'b') return 'a'; } void solve(int id,char a,char b) { char c1 = a,c2 = b; if(s[0] != a) pre[id][1] = 1; pre[id][2] = pre[id][1]; if(s[1] != b) pre[id][2]++; for(int i = 3;i <= n;++i) { char now = cal(c1,c2); pre[id][i] = pre[id][i - 1]; if(s[i - 1] != now) pre[id][i]++; c1 = c2,c2 = now; } } int main() { scanf("%d %d",&n,&m); cin >> s; solve(1,'a','b'); solve(2,'a','c'); solve(3,'b','a'); solve(4,'b','c'); solve(5,'c','a'); solve(6,'c','b'); while(m--) { int L,r;scanf("%d %d",&L,&r); if(L == r) printf("0\n"); else if(r - L == 1) printf("%d\n",s[L - 1] == s[r - 1] ? 1 : 0); else { LL ans = INF; for(int i = 1;i <= 6;++i) ans = min(ans,pre[i][r] - pre[i][L - 1]); printf("%lld\n",ans); } } // system("pause"); return 0; } /* 5 10 aaaac 1 5 */
E:这个题一开始的想法是在按线段左界排 ,在线段树上维护dp,但是如果用set维护答案,那么复杂度会过高。
正解:考虑尺取,这个真的很巧妙。
我们考虑维护一个下界对应的最小上界,所以我们将所有线段按权值来排序,然后尺取去加上线段,当满足覆盖到所有点之后。
说明我们维护的这段区间是以当前左界为下界的最小上界,这很显然。
对于所有点都被覆盖的情况,我们通过线段树去统计每个点被覆盖的次数,然后查询最小值即可,如果最小值 > 0,说明满足。
这里还有个细节就是,他这里其实是要线段覆盖,也就是说明每个点之间要有边连起来,那么我们就可以把每次维护的右端点看成是断开的。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 3e5 + 5; const int M = 1e6 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; int n,m; struct PT{ int L,r,w; bool operator < (const PT &a) const { return w < a.w; } }p[N]; struct Node{int L,r,tag,micnt;}node[M << 2]; void Pushup(int idx) { node[idx].micnt = min(node[idx << 1].micnt,node[idx << 1 | 1].micnt); } void Pushdown(int idx) { if(node[idx].tag != 0) { node[idx << 1].micnt += node[idx].tag; node[idx << 1 | 1].micnt += node[idx].tag; node[idx << 1].tag += node[idx].tag; node[idx << 1 | 1].tag += node[idx].tag; node[idx].tag = 0; } } void build(int L,int r,int idx) { node[idx].L = L,node[idx].r = r,node[idx].micnt = node[idx].tag = 0; if(L == r) { return ; } int mid = (L + r) >> 1; build(L,mid,idx << 1); build(mid + 1,r,idx << 1 | 1); Pushup(idx); } void update(int L,int r,int idx,int val) { if(node[idx].L >= L && node[idx].r <= r) { node[idx].micnt += val; node[idx].tag += val; return ; } Pushdown(idx); int mid = (node[idx].L + node[idx].r) >> 1; if(mid >= L) update(L,r,idx << 1,val); if(mid < r) update(L,r,idx << 1 | 1,val); Pushup(idx); } int main() { scanf("%d %d",&n,&m); for(int i = 1;i <= n;++i) scanf("%d %d %d",&p[i].L,&p[i].r,&p[i].w); sort(p + 1,p + n + 1); int ans = INF; if(m == 1) printf("0\n"); else { build(1,m - 1,1); int L = 1,r = 1; if(p[1].L != p[1].r) update(p[1].L,p[1].r - 1,1,1); while(L <= r && r <= n) { if(node[1].micnt > 0) { ans = min(ans,p[r].w - p[L].w); if(ans == 0) break; if(p[L].L != p[L].r) update(p[L].L,p[L].r - 1,1,-1); L++; } else { r++; if(p[r].L != p[r].r) update(p[r].L,p[r].r - 1,1,1); } } printf("%d\n",ans); } //system("pause"); return 0; }
F:这题很不错我觉得,而且解法好像有很多。
首先,我们考虑什么情况下可以加边。
如果该条边是两个不同连通块的边,那么很显然可以加入,这里用并查集判断即可。
这时我们可以发现一个很显然的结论,如果一条边能连通两个简单环,那么一定不能加入,因为另外两个简单环的异或和一定是权值一样的(因为都异或这条边 = 1)。
那么加入这条边后,两个简单环组成的大环异或和肯定 = 0。
也就是说对于一条边,它最多只能在一个简单环中。那么很显然是叫我们去维护一个仙人掌图。
我们考虑这个树形图。
对于这棵树,如果我们加入的这条边,连通的全部都是树边,那么就满足。如果连通的有非树边,那么就不满足。
我们可以考虑虚加入这条边,也就是计算上但是不真正加入到图中,那么我们维护的就会始终是一棵树。
所以我们需要支持一种操作,对所有非树边打上标记,且一次性查询一段树上的异或和。
对于一段树上异或和的查询,我们可以维护从树根开始的一个异或和。
那么对于树上任意两点的异或和 = dp[u] ^ dp[lca(u,v)] ^ (dp[v] ^ dp[lca(u,v)] = dp[u] ^ dp[v]。
可以发现我们甚至都不需要计算出lca就能实现维护。
对于非树边的打标记操作,有很多种做法,看很多大佬都用的LCT,但是本蒟蒻并不会。
我这里用了树剖来维护。
有很多细节需要注意:首先这里可能是很多的不同的树,所以需要不断增长下标来保证线段树的节点不会冲突.
id,rk这些数组也同理。
同时由于这里维护的是边的状态,那么我们可以用每个点来代表他与父节点连通的那条边的状态。
这样也会导致我们树剖跳链的时候会把lca代表的那条边算上,但实际上是不应该算上的。
这里我想到了两种处理方法,一种不直接跳到lca,而是最后倍增跳,第二种是我们再单独对lca进行一次处理。
我这里用了第二种做法,但是这里又可能出现前面被标记过然后再标记的情况,所以我们就需要去维护标记次数。
因为除了比较特殊的lca之外,其他点肯定只会被标记一次,所以我们对线段树上的叶子要做稍微不一样的处理。
最后再有亿点细节就解决了.
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 3e5 + 5; const int M = 5e5 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; int n,q,fa[N],ans[M]; int ssize[N],son[N],top[N],id[N],rk[N],ffa[N],tim = 0,tot = 0,vis[N],rt[N],dep[N],dp[N],st[N],ed[N]; struct Edge{int to,dis;}; vector<Edge> G[N]; int Find(int x) {return x == fa[x] ? x : fa[x] = Find(fa[x]);} struct PT{int u,v,x;}p[M]; void dfs1(int u,int fa) { ssize[u] = 1; vis[u] = tot; ffa[u] = fa; dep[u] = dep[fa] + 1; for(auto v : G[u]) { if(v.to == fa) continue; dp[v.to] = dp[u] ^ v.dis; dfs1(v.to,u); ssize[u] += ssize[v.to]; if(ssize[v.to] > ssize[son[u]]) son[u] = v.to; } } void dfs2(int u,int fa,int sta) { id[u] = ++tim; rk[tim] = u; top[u] = sta; if(son[u] == 0) return ; dfs2(son[u],u,sta); for(auto v : G[u]) { if(v.to == fa || v.to == son[u]) continue; dfs2(v.to,u,v.to); } } int mx; struct Node{int L,r,cnt,tag;}node[N << 2]; void Pushup(int idx) { node[idx].cnt = node[idx << 1].cnt + node[idx << 1 | 1].cnt; } void Pushdown(int idx) { if(node[idx].tag != 0) { if(node[idx << 1].L == node[idx << 1].r) node[idx << 1].cnt++; else node[idx << 1].cnt = node[idx << 1].r - node[idx << 1].L + 1; if(node[idx << 1 | 1].L == node[idx << 1 | 1].r) node[idx << 1 | 1].cnt++; else node[idx << 1 | 1].cnt = node[idx << 1 | 1].r - node[idx << 1 | 1].L + 1; node[idx << 1].tag = node[idx << 1 | 1].tag = 1; node[idx].tag = 0; } } void build(int L,int r,int idx) { mx = max(mx,idx); node[idx].L = L,node[idx].r = r,node[idx].cnt = node[idx].tag = 0; if(L == r) return ; int mid = (L + r) >> 1; build(L,mid,idx << 1); build(mid + 1,r,idx << 1 | 1); Pushup(idx); } void update(int L,int r,int idx) { if(node[idx].L >= L && node[idx].r <= r) { if(node[idx].L == node[idx].r) node[idx].cnt++; else node[idx].cnt = node[idx].r - node[idx].L + 1; node[idx].tag = 1; return ; } Pushdown(idx); int mid = (node[idx].L + node[idx].r) >> 1; if(mid >= L) update(L,r,idx << 1); if(mid < r) update(L,r,idx << 1 | 1); Pushup(idx); } void upd(int x,int idx) { if(node[idx].L == node[idx].r) { node[idx].cnt--; return ; } Pushdown(idx); int mid = (node[idx].L + node[idx].r) >> 1; if(mid >= x) upd(x,idx << 1); else upd(x,idx << 1 | 1); Pushup(idx); } int query(int L,int r,int idx) { if(node[idx].L >= L && node[idx].r <= r) return node[idx].cnt; int mid = (node[idx].r + node[idx].L) >> 1,ans = 0; Pushdown(idx); if(mid >= L) ans += query(L,r,idx << 1); if(mid < r) ans += query(L,r,idx << 1 | 1); return ans; } int qy(int x,int idx) { if(node[idx].L == node[idx].r) return node[idx].cnt; int mid = (node[idx].r + node[idx].L) >> 1; Pushdown(idx); if(mid >= x) return qy(x,idx << 1); else return qy(x,idx << 1 | 1); } pii Tree_query(int x,int y) { int ans = 0; while(top[x] != top[y]) { if(dep[top[x]] < dep[top[y]]) swap(x,y); ans += query(id[top[x]],id[x],rt[vis[x]]); x = ffa[top[x]]; } if(dep[x] > dep[y]) swap(x,y); ans += query(id[x],id[y],rt[vis[x]]); ans -= qy(id[x],rt[vis[x]]); return pii{ans,x}; } void Tree_update(int x,int y) { while(top[x] != top[y]) { if(dep[top[x]] < dep[top[y]]) swap(x,y); update(id[top[x]],id[x],rt[vis[x]]); x = ffa[top[x]]; } if(dep[x] > dep[y]) swap(x,y); update(id[x],id[y],rt[vis[x]]); upd(id[x],rt[vis[x]]); } int main() { scanf("%d %d",&n,&q); for(int i = 1;i <= n;++i) fa[i] = i; for(int i = 1;i <= q;++i) { scanf("%d %d %d",&p[i].u,&p[i].v,&p[i].x); int xx = Find(p[i].u),yy = Find(p[i].v); if(xx != yy) { fa[xx] = yy; ans[i] = 1; G[p[i].u].push_back(Edge{p[i].v,p[i].x}); G[p[i].v].push_back(Edge{p[i].u,p[i].x}); } } for(int i = 1;i <= n;++i) { if(vis[i]) continue; st[++tot] = tim + 1; dfs1(i,0); dfs2(i,0,i); ed[tot] = tim; rt[tot] = mx + 1; mx = 0; build(st[tot],ed[tot],rt[tot]); } for(int i = 1;i <= q;++i) { if(ans[i] == 1) continue; pii ma = Tree_query(p[i].u,p[i].v); if(ma.first == 0 && (dp[p[i].u] ^ dp[p[i].v] ^ p[i].x) == 1) { ans[i] = 1; Tree_update(p[i].u,p[i].v); } //printf("u is %d v is %d ma.first is %d ans[%d] is %d\n",p[i].u,p[i].v,ma.first,i,ans[i]); } for(int i = 1;i <= q;++i) printf("%s\n",ans[i] ? "YES" : "NO"); system("pause"); return 0; }