暑假集训csp提高模拟19
赛时rank 5,T1 100,T2 100,T3 20,T4 5
T4 暴力可过?数据这么水?
咋还有失恋舔狗三部曲啊
T1 数字三角形
相对简单的构造题。
能向上走就向上走,不能的话往左走,再不能的话就往下走,可以证明一定不会往右走。
递归写就行
点此查看代码
#include<bits/stdc++.h> #include<bits/extc++.h> // using namespace __gnu_pbds; // using namespace __gnu_cxx; using namespace std; #define infile(x) freopen(x,"r",stdin) #define outfile(x) freopen(x,"w",stdout) #define errfile(x) freopen(x,"w",stderr) #ifdef LOCAL FILE *InFile = infile("in.in"),*OutFile = outfile("out.out"); // FILE *ErrFile=errfile("err.err"); #else FILE *Infile = stdin,*OutFile = stdout; //FILE *ErrFile = stderr; #endif using ll=long long;using ull=unsigned long long; using db = double;using ldb = long double; const int N = 510; int n,a[N],ans[N][N]; bitset<N> vis[N]; int limit; inline bool in_range(int x,int y){ return 1 <= y && y <= n && 1 <= x && x <= n; } bool insert(int num,int x,int y,int have){ if(!have) return true; ans[x][y] = num; vis[x][y] = true; if(have == 1) return true; bool flag = false; if(!vis[x-1][y] && in_range(x-1,y) && x - 1 >= limit){ flag = insert(num,x-1,y,have-1); } if(flag) return true; if(!vis[x][y-1] && in_range(x,y-1)){ flag = insert(num,x,y-1,have-1); } if(flag) return true; if(!vis[x+1][y] && in_range(x+1,y)){ flag = insert(num,x+1,y,have-1); } if(flag) return true; return false; } inline void solve(){ cin>>n; for(int i = 1;i <= n; ++i) cin>>a[i]; for(int i = 1;i <= n; ++i){ limit = i; insert(a[i],i,i,a[i]); } // for(int i = 1;i <= n; ++i){ // for(int j = 1;j <= n; ++j){ // cerr<<vis[i][j]<<' '; // } // cerr<<'\n'; // } for(int i = 1;i <= n; ++i){ for(int j = 1;j <= i; ++j){ cout<<ans[i][j]<<' '; } cout<<'\n'; } } signed main(){ cin.tie(nullptr)->sync_with_stdio(false); cout.tie(nullptr)->sync_with_stdio(false); solve(); }
T2 那一天她离我而去
就是找最小环的板子。
暴力是枚举与1相连的边并断开,求1到另一个点的最短路,环的长度就是
随机数据非常难卡,甚至spfa也可以过。
官方题解是二进制分组,写起来麻烦,复杂度又不优秀(反正我不会)。
来一个正常的求最小环做法——建最短路树。
将一个图以1为根的最短路树建出来,然后枚举所有的非树边。
如果边的两端在以1为根的同一颗子树里,不统计答案,反之,答案为
取个min即可。
话说网上找到的题解都是二进制分组的
时间复杂度
点此查看代码
#include<bits/stdc++.h> #include<bits/extc++.h> // using namespace __gnu_pbds; // using namespace __gnu_cxx; using namespace std; #define infile(x) freopen(x,"r",stdin) #define outfile(x) freopen(x,"w",stdout) #define errfile(x) freopen(x,"w",stderr) #ifdef LOCAL FILE *InFile = infile("in.in"),*OutFile = outfile("out.out"); // FILE *ErrFile=errfile("err.err"); #else FILE *Infile = stdin,*OutFile = stdout; //FILE *ErrFile = stderr; #endif using ll=long long;using ull=unsigned long long; using db = double;using ldb = long double; const int N = 1e4 + 10; struct EDGE{int to,next,w;}edge[N<<4]; int head[N],cnt; inline void add(int u,int v,int w){ edge[++cnt] = {v,head[u],w}; head[u] = cnt; } int n,m; vector<int> son[N]; bitset<N> vis; int dist[N],fa[N]; inline void dijkstra(int s){ vis.reset(); for(int i = 1;i <= n; ++i)dist[i] = 0x3f3f3f3f; __gnu_pbds::priority_queue<pair<int,int>,greater<pair<int,int> > > q; dist[s] = 0; q.push(make_pair(dist[s],s)); while(q.size()){ int x = q.top().second;q.pop(); if(vis[x]) continue; vis[x] = true; for(int i = head[x]; i;i = edge[i].next){ int y = edge[i].to; if(dist[y] > dist[x] + edge[i].w){ dist[y] = dist[x] + edge[i].w; q.push(make_pair(dist[y],y)); fa[y] = x; } } } } int belong[N]; void dfs(int x,int z){ belong[x] = z; for(auto i : son[x]) dfs(i,z); } int u[N<<2],v[N<<2],w[N<<2]; inline void solve(){ int T;cin>>T; memset(dist,0x3f,sizeof dist); while(T--){ cin>>n>>m; for(int i = 1;i <= m; ++i){ cin>>u[i]>>v[i]>>w[i]; add(u[i],v[i],w[i]);add(v[i],u[i],w[i]); } dijkstra(1); for(int i = 2;i <= n; ++i) son[fa[i]].emplace_back(i); int ans = 0x3f3f3f3f; for(auto i : son[1]) dfs(i,i); belong[1] = 1; for(int i = 1;i <= m; ++i){ int x = u[i],y = v[i],W = w[i]; if(belong[x] == belong[y] || !belong[x] || !belong[y] || fa[y] == x || fa[x] == y)continue; ans = min(ans,dist[x] + dist[y] + W); } cout<<(ans == 0x3f3f3f3f?-1:ans)<<'\n'; for(int i = 1;i <= n; ++i) head[i] = 0; for(int i = 1;i <= n; ++i) belong[i] = fa[i] = 0; for(int i = 1;i <= n; ++i) vector<int> ().swap(son[i]); cnt = 0; } } signed main(){ cin.tie(nullptr)->sync_with_stdio(false); cout.tie(nullptr)->sync_with_stdio(false); solve(); }
T3 哪一天她能重回我身边
暴力搜索随便拿20的分。
正解换根dp。
建图方式比较巧妙,将正面的数与反面的数建边,但也不是不能想到(然后我就去想二分图了结果发现不会做然后打了暴力二十遗憾离场)。
当且仅当每个点的出度都
突然不想打T3了,明天还要干知识点。
先存着,挂一下官方题解,学一下建图的trick
T4 单调区间
感觉官方题解比较抽象,所以没有看。
但可以挂一下官方题解。
挂一个比较像的题Two Merged Sequences
简化一下题意,就是找有多少个子段可以划分成一个降序的和一个升序的。
我们应用刚刚挂起来的题的结果,大力分讨贪心。
然后就有了一个
点此查看代码
#include<bits/stdc++.h> #include<bits/extc++.h> // using namespace __gnu_pbds; // using namespace __gnu_cxx; using namespace std; #define infile(x) freopen(x,"r",stdin) #define outfile(x) freopen(x,"w",stdout) #define errfile(x) freopen(x,"w",stderr) #ifdef LOCAL FILE *InFile = infile("in.in"),*OutFile = outfile("out.out"); // FILE *ErrFile=errfile("err.err"); #else FILE *Infile = stdin,*OutFile = stdout; //FILE *ErrFile = stderr; #endif using ll=long long;using ull=unsigned long long; using db = double;using ldb = long double; const int N = 2e5 + 10; int n,a[N],ans; inline void solve(){ cin>>n; for(int i = 1;i <= n; ++i) cin>>a[i]; for(int l = 1;l <= n; ++l){ for(int r = l;r <= n; ++r){ int mn = INT_MAX,mx = INT_MIN; bool flag = true; swap(a[r+1],a[n+1]); for(int i = l;i <= r; ++i){ if(a[i] < mn && a[i] > mx){ if(a[i + 1] > a[i]) mx = a[i]; else mn = a[i]; } else if(a[i] > mx) mx = a[i]; else if(a[i] < mn) mn = a[i]; else {flag = false;break;} } swap(a[r+1],a[n+1]); if(flag) ans++; } } cout<<ans; } signed main(){ cin.tie(nullptr)->sync_with_stdio(false); cout.tie(nullptr)->sync_with_stdio(false); solve(); }
通过观察性质暴力打表,我们发现了一个性质
如果有两个区间
然后就可以优化一下这个暴力,在学校oj的水数据上可以通过
点此查看代码
#include<bits/stdc++.h> #include<bits/extc++.h> // using namespace __gnu_pbds; // using namespace __gnu_cxx; using namespace std; #define infile(x) freopen(x,"r",stdin) #define outfile(x) freopen(x,"w",stdout) #define errfile(x) freopen(x,"w",stderr) #ifdef LOCAL FILE *InFile = infile("in.in"),*OutFile = outfile("out.out"); // FILE *ErrFile=errfile("err.err"); #else FILE *Infile = stdin,*OutFile = stdout; //FILE *ErrFile = stderr; #endif using ll=long long;using ull=unsigned long long; using db = double;using ldb = long double; const int N = 2e5 + 10; int n,a[N],ans; bool pd[N]; inline void solve(){ cin>>n; for(int i = 1;i <= n; ++i) cin>>a[i]; for(int l = n;l >= 1; --l){ for(int r = l;r <= n; ++r){ if(pd[r]) break; int mn = INT_MAX,mx = INT_MIN; bool flag = true; swap(a[r+1],a[n+1]); for(int i = l;i <= r; ++i){ if(a[i] < mn && a[i] > mx){ if(a[i + 1] > a[i]) mx = a[i]; else mn = a[i]; } else if(a[i] > mx) mx = a[i]; else if(a[i] < mn) mn = a[i]; else {flag = false;break;} } swap(a[r+1],a[n+1]); if(flag) ans++; else pd[r] = true; } } cout<<ans; } signed main(){ cin.tie(nullptr)->sync_with_stdio(false); cout.tie(nullptr)->sync_with_stdio(false); solve(); }
然后我们接着考虑优化这个暴力,我们成功的发现 :
你猜猜是什么性质?实在猜不出来再点哦!
这玩意压根没法优化
然后我们就可以利用上述性质得出正解了。
如果存在一段区间
所以若
那么就可以考虑分治,对于区间
最后的答案为
时间复杂度难以证明,只能感性理解一下大约在
如果有会证明的佬可以解释一下捏
点此查看代码
#include<bits/stdc++.h> #include<bits/extc++.h> // using namespace __gnu_pbds; // using namespace __gnu_cxx; using namespace std; #define infile(x) freopen(x,"r",stdin) #define outfile(x) freopen(x,"w",stdout) #define errfile(x) freopen(x,"w",stderr) #ifdef LOCAL FILE *InFile = infile("in.in"),*OutFile = outfile("out.out"); // FILE *ErrFile=errfile("err.err"); #else FILE *Infile = stdin,*OutFile = stdout; //FILE *ErrFile = stderr; #endif using ll=long long;using ull=unsigned long long; using db = double;using ldb = long double; const int N = 2e5 + 10; int n,a[N],ans[N]; inline void dp(int now){ int mn = INT_MAX,mx = INT_MIN; for(int i = now;i <= n; ++i){ if(a[i] < mn && a[i] > mx){ if(a[i + 1] > a[i]) mx = a[i]; else mn = a[i]; } else if(a[i] < mn) mn = a[i]; else if(a[i] > mx) mx = a[i]; else return ans[now] = i - 1,void(); } ans[now] = n; } void work(int l,int r){ if(l + 1 >= r) return; if(ans[l] == ans[r]){ for(int i = l;i <= r; ++i) ans[i] = ans[l]; return; } int mid = (l+r) >> 1; dp(mid); work(l,mid);work(mid,r); }; inline void solve(){ cin>>n; for(int i = 1;i <= n; ++i) cin>>a[i]; dp(1),dp(n); work(1,n); ll res = 0; for(int i = 1;i <= n; ++i) res += ans[i]; cout<<res - 1ll * n * (n - 1)/2<<'\n'; } signed main(){ cin.tie(nullptr)->sync_with_stdio(false); cout.tie(nullptr)->sync_with_stdio(false); solve(); }
本文来自博客园,作者:CuFeO4,转载请注明原文链接:https://www.cnblogs.com/hzoi-Cu/p/18355575
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】