出题人卡的真的好.
宇佐大人的白旗 ~ Most Famous Hero:
这题有点无语了,比赛的时候数据出问题了,害我一直出问题,最后数据修了就过了。
做法1:直接最短路上更新就好了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<LL,int> pii; const int N = 1e4 + 5; const int M = 2e3 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e18 #define dbg(ax) cout << "now this num is " << ax << endl; namespace FASTIO{ inline int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } } using namespace FASTIO; int n,m,c[N],p[N],s,t; LL dis[N]; struct Node{int to,dis;}; vector<Node> G[N]; void solve() { for(int i = 1;i <= n;++i) dis[i] = INF; priority_queue<pii,vector<pii>,greater<pii> > Q; dis[s] = c[s] - p[s]; Q.push(pii{dis[s],s}); while(!Q.empty()) { int u = Q.top().second; LL d = Q.top().first; Q.pop(); if(d > dis[u]) continue; for(auto v : G[u]) { LL tmp = dis[u] + v.dis; LL ma = tmp + c[v.to] - p[v.to]; if(v.to == t || ma < tmp) tmp = ma; if(tmp < dis[v.to]) { dis[v.to] = tmp; Q.push(pii{dis[v.to],v.to}); } } } } int main() { n = read(),m = read(); for(int i = 1;i <= n;++i) c[i] = read(); for(int i = 1;i <= n;++i) p[i] = read(); while(m--) { int u,v,w;u = read(),v = read(),w = read(); G[u].push_back(Node{v,w}); } s = read(),t = read(); solve(); printf("%lld ",dis[t]); // system("pause"); return 0; }
做法2:当数据量变大,做法1的复杂度可能就会比较高,因为这条线路一定从S出发,到T结束。
那么就以S为根来建一棵树,注意是有向的,也就是有向无环图,然后就可以拓扑排序了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<LL,int> pii; const int N = 1e4 + 5; const int M = 2e3 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e18 #define dbg(ax) cout << "now this num is " << ax << endl; namespace FASTIO{ inline int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } } using namespace FASTIO; int n,m,c[N],p[N],s,t,in[N],a[N]; LL dis[N]; bool vis[N]; struct Node{int to,dis;}; vector<Node> G[N],vec[N]; void dfs(int u) { vis[u] = 1; for(auto v : G[u]) { if(!vis[v.to]) { dfs(v.to); } } } void solve() { for(int i = 1;i <= n;++i) dis[i] = INF; queue<int> Q; Q.push(s); dis[s] = 0; while(!Q.empty()) { int u = Q.front(); Q.pop(); for(auto v : vec[u]) { in[v.to]--; LL ma = dis[u] + v.dis; if(v.to != s && v.to != t && a[v.to] <= 0) ma += a[v.to]; dis[v.to] = min(dis[v.to],ma); if(in[v.to] == 0) { Q.push(v.to); } } } } int main() { n = read(),m = read(); for(int i = 1;i <= n;++i) c[i] = read(); for(int i = 1;i <= n;++i) p[i] = read(),a[i] = c[i] - p[i]; while(m--) { int u,v,w;u = read(),v = read(),w = read(); G[u].push_back(Node{v,w}); } s = read(),t = read(); dfs(s); for(int i = 1;i <= n;++i) { if(vis[i] == 0) continue; for(auto v : G[i]) { if(vis[v.to] == 0) continue; vec[i].push_back(Node{v.to,v.dis}); in[v.to]++; } } solve(); printf("%lld ",dis[t] + a[s] + a[t]); // system("pause"); return 0; }
飘上月球不死之烟 ~ Elixir of Life:
很裸的线段树,但是这里内存卡的很紧,如果用结构体来建树,就会爆内存。
所以就用数组来,同时,这里普通的线段树常数卡的好,也能过。
我们考虑对线段树进行一些优化。
首先很显然可以想到倒着进行修改,那么一个被修改过的位置,它的大小写就固定了。我们就打上标记。
对于一个位置最多修改一下,这样平推下来复杂度就是nlogn。
同时最后我们一直query就下放所有颜色, 这样比遍历然后单点询问要快一点。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<LL,int> pii; const int N = 1e6 + 5; const int M = 2e3 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e18 #define dbg(ax) cout << "now this num is " << ax << endl; #define DBG(bx,ax) cout << bx << " is " << ax << endl; namespace FASTIO{ inline int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } } using namespace FASTIO; int m,tag[N << 2],k[N << 2],sum[N << 2]; int id[N],L[N],r[N]; void Pushup(int idx) { sum[idx] = sum[idx << 1] + sum[idx << 1 | 1]; } void Pushdown(int L,int r,int idx) { if(tag[idx] != 0) { int mid = (L + r) >> 1; if(sum[idx << 1] != mid - L + 1) { sum[idx << 1] = mid - L + 1; tag[idx << 1] = tag[idx]; k[idx << 1] = tag[idx]; } if(sum[idx << 1 | 1] != r - mid) { sum[idx << 1 | 1] = r - mid; tag[idx << 1 | 1] = tag[idx]; k[idx << 1 | 1] = tag[idx]; } tag[idx] = 0; } } void update(int le,int ri,int L,int r,int idx,int val) { if(le >= L && ri <= r) { tag[idx] = k[idx] = val; sum[idx] = ri - le + 1; return ; } Pushdown(le,ri,idx); int mid = (le + ri) >> 1; if(mid >= L) { if(sum[idx << 1] != mid - le + 1) { update(le,mid,L,r,idx << 1,val); } } if(mid < r) { if(sum[idx << 1 | 1] != ri - mid) update(mid + 1,ri,L,r,idx << 1 | 1,val); } Pushup(idx); } void query(int L,int r,int idx) { if(L == r) { id[L] = k[idx]; return ; } Pushdown(L,r,idx); int mid = (L + r) >> 1; if(sum[idx << 1] != 0) query(L,mid,idx << 1); if(sum[idx << 1 | 1] != 0) query(mid + 1,r,idx << 1 | 1); } char cal1(char c) { if(c >= 'a' && c <= 'z') return c; else return c + 32; } char cal2(char c) { if(c >= 'A' && c <= 'Z') return c; else return c - 32; } int main() { m = read(); string s;getline(cin,s); int n = s.size(); for(int i = 1;i <= m;++i) id[i] = read(),L[i] = min(n,read()),r[i] = min(n,read()); for(int i = m;i >= 1;--i) { update(1,n,L[i],r[i],1,id[i]); } memset(id,0,sizeof(id)); query(1,n,1); for(int i = 1;i <= n;++i) { //printf("i is %d val is %d ",i,id[i]); if(s[i - 1] == ' ') printf(" "); else { if(id[i] == 0) printf("%c",s[i - 1]); else if(id[i] == 1) printf("%c",cal1(s[i - 1])); else if(id[i] == 2) printf("%c",cal2(s[i - 1])); else { if(i == 1) printf("%c",cal2(s[i - 1])); else if(s[i - 2] == ' ') printf("%c",cal2(s[i - 1])); else printf("%c",cal1(s[i - 1])); } } } //system("pause"); return 0; } /* 5 ABC DADN A adi da D 1 3 3 1 3 10 3 3 10 2 2 10 1 2 2 */
Locked Girl ~ 少女密室:
那么我们可以考虑当前完成了所有删除操作,然后只需要添加操作即可。
显然如果要删除后的代码1,添加一些就可以得到代码2。
那么这段删除后的代码,一定是代码1和代码2的公共子序列。
假设删去的长度为x,代码1长度len1,代码2长度len2。
那么对于需要添加的字符串长度就是len2 - (len1 - x) = len1 - len2 + x就是操作的数量。
那么我们需要操作的次数就是len1 - len2 + 2 * x.
显然x越小,我们操作的次数越小,x越小也就是表示要公共子序列越大,那么很显然就是求最长公共子序列了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<LL,int> pii; const int N = 1e6 + 5; const int M = 2e3 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e18 #define dbg(ax) cout << "now this num is " << ax << endl; #define DBG(bx,ax) cout << bx << " is " << ax << endl; namespace FASTIO{ inline int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } } using namespace FASTIO; int dp[305][305],n,m; string a[305],b[305]; int main() { n = read(); for(int i = 1;i <= n;++i) getline(cin,a[i]); m = read(); for(int i = 1;i <= m;++i) getline(cin,b[i]); for(int i = 1;i <= n;++i) { for(int j = 1;j <= m;++j) { if(a[i] == b[j]) dp[i][j] = max(dp[i][j],dp[i - 1][j - 1] + 1); else dp[i][j] = max(dp[i - 1][j],dp[i][j - 1]); } } int ans = n - dp[n][m] + m - dp[n][m]; printf("%d ",ans); // system("pause"); return 0; }
回忆京都 ~ Retrospective Kyoto:
这题还是有点意思的,要推到结论需要思考时间。
首先,对于该图的任意一棵生成树tree(所有点都在该树上),都必定存在一种方法满足条件。
方法就是从叶子开始往上走,每次都从父亲走到儿子,注意要一个父节点的所有儿子都走完,再将这个父节点往上。
dfs来找一棵生成树即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 1e6 + 5; const int M = 2e3 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e18 #define dbg(ax) cout << "now this num is " << ax << endl; #define DBG(bx,ax) cout << bx << " is " << ax << endl; namespace FASTIO{ inline int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } } using namespace FASTIO; vector<int> G[N]; bool vis[N]; vector<pii> ans; void dfs(int u) { vis[u] = 1; for(auto v : G[u]) { if(!vis[v]) { ans.push_back(pii{u,v}); dfs(v); } } } int main() { int n,m;n = read(),m = read(); while(m--) { int u,v;u = read(),v = read(); G[u].push_back(v); G[v].push_back(u); } dfs(1); if(ans.size() != n - 1) printf("Impossible! "); else { for(int i = ans.size() - 1;i >= 0;--i) { printf("%d -> %d ",ans[i].first,ans[i].second); } } //system("pause"); return 0; }
青木原的传说 ~ Forbidden Forest:
这题卡vector,最后用了链式前向星才过的。
一开始看成连通块一起减导致贪心错误,换根的思路也误导了自己。
这里正确的贪心思路应该是从叶子节点往上。
我们考虑对于节点u,它的子树已经完成了归0,那么节点u它的消去路径,只有两种情况。
1:从u往fa走的一条链。
2:从u到fa另一个子节点v的一条链。
这里方案2一次可以减少两个儿子的1,然后方案1只能减少一个儿子的1,所以方案2肯定比方案1优。
那么我们在能用方案2的时候就尽量用方案2.
将所有子节点的贡献放入大顶堆,大顶堆能保证我们最后实行方案1,可操作的子节点值是最大的,这样就能最大化利用u的这条链。
这里在堆里操作的时候每次都要一个一个减,然后重新放入堆,这样也是为了保证最后得到的堆顶的子节点值是当前最优的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<LL,int> pii; const int N = 5e5 + 5; const int M = 2e3 + 5; const LL Mod = 100000007; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; int n,m,a[N]; LL ans = 0; bool vis[N]; int head[N],tot = 0; struct Node{int to,next;}e[N << 1]; void add(int u,int v) { e[++tot].to = v,e[tot].next = head[u],head[u] = tot; e[++tot].to = u,e[tot].next = head[v],head[v] = tot; } void dfs(int u,int fa) { vis[u] = 1; priority_queue<int,vector<int>,less<int> > Q; for(int i = head[u];i;i = e[i].next) { int v = e[i].to; if(v == fa) continue; dfs(v,u); if(a[v] != 0) Q.push(a[v]); } while(a[u] != 0 && Q.size() > 1) { int x = Q.top(); Q.pop(); int y = Q.top(); Q.pop(); x--,y--,ans--,a[u]--; if(x != 0) Q.push(x); if(y != 0) Q.push(y); } ans += a[u]; if(Q.size() > 0) { int x = Q.top(); Q.pop(); if(a[u] >= x) ans -= x; else ans -= a[u]; } } int main() { scanf("%d %d",&n,&m); for(int i = 1;i <= n;++i) scanf("%d",&a[i]); while(m--) { int u,v;scanf("%d %d",&u,&v); add(u,v); } for(int i = 1;i <= n;++i) { if(vis[i] == 1) continue; dfs(i,0); } printf("%lld ",ans); system("pause"); return 0; }