暑假集训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(n\log n+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。

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

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

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

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

image

T4 单调区间

Decinc Dividing

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

但可以挂一下官方题解。

image

挂一个比较像的题Two Merged Sequences

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

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

然后就有了一个\(O(n^3)\)的暴力

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

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

如果有两个区间\([l_1,r],[l_2,r](l_1<l_2)\),如果\([l_2,r]\)不合法,那么\([l_1,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]\)合法,那么\(\forall l\le i\le j\le r\)\([i,j]\)均为合法区间

所以若\(ans_i\)是最大的满足\([i,ans_i]\)为合法区间正整数,那么\(ans_i\ge ans_{i-1}\)

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

最后的答案为\(\sum_{i=1}^nans_i\,-\frac{n\times (n-1)}{2}\)

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

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

点此查看代码
#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 @ 2024-08-12 21:17  CuFeO4  阅读(47)  评论(7)    收藏  举报