暑假集训csp提高模拟19

赛时rank 5,T1 100,T2 100,T3 20,T4 5

T4 暴力可过?数据这么水?

咋还有失恋舔狗三部曲啊

T1 数字三角形

Fillomino 2

相对简单的构造题。

能向上走就向上走,不能的话往左走,再不能的话就往下走,可以证明一定不会往右走。

递归写就行

点此查看代码
#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到另一个点的最短路,环的长度就是dist[y]+edge[i].w,取min即可。

随机数据非常难卡,甚至spfa也可以过。

官方题解是二进制分组,写起来麻烦,复杂度又不优秀(反正我不会)。

来一个正常的求最小环做法——建最短路树。

将一个图以1为根的最短路树建出来,然后枚举所有的非树边。

如果边的两端在以1为根的同一颗子树里,不统计答案,反之,答案为dist[x]+dist[y]+edge[i].w

取个min即可。

话说网上找到的题解都是二进制分组的

时间复杂度O(T(nlogn+m))

点此查看代码
#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。

建图方式比较巧妙,将正面的数与反面的数建边,但也不是不能想到(然后我就去想二分图了结果发现不会做然后打了暴力二十遗憾离场)。

当且仅当每个点的出度都1时才有一个合法解。

突然不想打T3了,明天还要干知识点。

先存着,挂一下官方题解,学一下建图的trick

image

T4 单调区间

Decinc Dividing

感觉官方题解比较抽象,所以没有看。

但可以挂一下官方题解。

image

挂一个比较像的题Two Merged Sequences

简化一下题意,就是找有多少个子段可以划分成一个降序的和一个升序的。

我们应用刚刚挂起来的题的结果,大力分讨贪心。

然后就有了一个O(n3)的暴力

点此查看代码
#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();
}

通过观察性质暴力打表,我们发现了一个性质

如果有两个区间[l1,r],[l2,r](l1<l2),如果[l2,r]不合法,那么[l1,r]也一定不合法。

然后就可以优化一下这个暴力,在学校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();
}

然后我们接着考虑优化这个暴力,我们成功的发现 :

你猜猜是什么性质?实在猜不出来再点哦!
这玩意压根没法优化

然后我们就可以利用上述性质得出正解了。

如果存在一段区间[l,r]合法,那么lijr[i,j]均为合法区间

所以若ansi是最大的满足[i,ansi]为合法区间正整数,那么ansiansi1

那么就可以考虑分治,对于区间[l,r],我们先求出ansl,ansr,如果ansl=ansr,那么该区间内的ans均为ansl,反之,递归处理。

最后的答案为i=1nansin×(n1)2

时间复杂度难以证明,只能感性理解一下大约在nlogn级别的。

如果有会证明的佬可以解释一下捏

点此查看代码
#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();
}
posted @   CuFeO4  阅读(41)  评论(7编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示