省选联测1

hahaha,太菜了,成功垫底力。

rank 3,T1 100pts,T2 0 pts,T3 0pts.

T1好像是神必贪心,写了个不知道对不对的做法,反正过了大样例,就这样吧。

T2不会,写了个\(2^n\times n\log m\)的暴力,可能是dp or 图论?

T3没读懂题。

总结:菜。

upd:T1过了,T2边权只有0/1我打什么dijkstra啊?T3是送分题?

interval

题意简述

\(n(n\le 5\times 10^5)\) 个区间,求最多能选出多少对区间,使得每对中的两个区间不交,值域\(10^9\)

solution

考虑当前有一个区间\(c[l_c,r_c]\),如果有能和它匹配的,那么直接匹配,反之,那么看它能不能抢别人的。

考虑什么时候它能抢别人的。假设有两个区间\(b[l_b,r_b],a[l_a,r_a]\)已经配对\((r_a<l_b,r_a<l_c)\),假如\(c\)抢了\(a\)更优,那么当且仅当\(r_b<r_c\)。证明的话,考虑贪心,如果又有一个区间\(d[l_d,r_d]\),若\(r_b<r_c<l_d\),那么抢不抢都一样,若\(r_b<l_d<r_c\),那么抢了以后\(b\)就可以和\(d\)配对更优,若\(l_d<r_b<r_c\),那么拿不拿无影响。综上,此时\(c\)\(b\)一定不劣。

然后用个优先队列维护就行,时间复杂度\(O(n\log n)\)

code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout);
    // auto Err = freopen("err.err","w",stderr);
#else
    auto I = freopen("interval.in","r",stdin),O = freopen("interval.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = long double;using ldb = long double;
#define pii pair<int,int>
#define mk make_pair
#define ep emplace
const int N = 1e6 + 10;
int n;pii a[N];
struct node{
    int r,id,to;
    node(){};node(int R,int I,int T):r(R),id(I),to(T){}
    bool operator < (const node &x) const {return r == x.r?id < x.id:r < x.r;}
};set<node> st;
priority_queue<pii,vector<pii>,greater<pii> > uud;
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cin>>n;rep(i,1,n,1) cin>>a[i].first>>a[i].second;
    sort(a+1,a+1+n,[](pii a,pii b){return a.first == b.first?a.second < b.second:a.first < b.first;});
    rep(i,1,n,1){
        bool flag = false;
        if(st.empty() && uud.empty()){uud.ep(a[i].second,i);continue;}
        else if(uud.size()){
            if(uud.top().first < a[i].first){
                int p = uud.top().second;
                st.ep(a[i].second,i,p);
                uud.pop();flag = true;
            }
        }
        if(flag) continue;
        if(st.size()){
            auto it = st.begin();
            if(it->r > a[i].second){uud.ep(a[i].second,i);continue;}
            int id1 = it->id,id2 = it->to;
            st.erase(node(a[id1].second,id1,id2));
            uud.emplace(a[id1].second,id1);
            st.ep(a[i].second,i,id2);
        }
        else uud.ep(a[i].second,i);
    }
    cout<<st.size()<<'\n';
}

apers

题意简述

给定一个\(n(n\le 200)\)个节点的有向图\(G\),有\(m(m\le 500)\)条边,每个节点被标记的代价为\(c_i(c_i\le 10^7)\),求出从\(s\rightarrow t\)的所有路径上,至少有\(k(k\le 5)\)个节点被标记的最小代价。

solution

最小割板子。

发现\(k\)很小,考虑建分层图,第\(i\)层表示踩了\(i\)颗雷。那么就要保证踩完一颗雷后就向上移动一层,所以第\(i\)层的\(x\)和第\(i+1\)层的\(x\)连一条\(inf\)的边。

但是这是不完善的,还没有将权值赋进去,考虑拆点,具体的,将第\(i\)层的点\(x\)拆成两个点\((x,i,0),(x,i,1)\),将\((x,i,0)\)\((x,i,1)\)中连一条\(c_x\)的边。

显然不会重复踩同一颗雷,所以将\((x,i,1),(x,i+1,1)\)之间连一条\(inf\)的边。走到终点后不会继续走,所以将所有的\((t,i,0)\)\((t,k,0)\)连一条\(inf\)的边,然后跑一个以\((s,1,0)\)为源点,以\((t,k,1)\)为汇点的最小割就行了。

算出来是过不去的,但是信仰一发就过了。

code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout);
#else
    // auto I = stdin,O = stdout;
    auto I = freopen("apers.in","r",stdin),O = freopen("apers.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = long double;using ldb = long double;
const int N = 210,M = 510;
template<size_t N,size_t M>
struct Graph{
    struct Edge{int to,n;ll w;}e[M];
    int h[N],ct = 1;
    void add(int u,int v,ll w){e[++ct] = {v,h[u],w};h[u] = ct;}
#define repe(i,x,y,g) for(int i = g.h[x],y = g.e[i].to;i;i = g.e[i].n,y = g.e[i].to)
};Graph<N*100,M*10000> g;
vector<int> e[N];
int n,m,k,s,t,c[N];bool pd[N][N];
int mp[N][10][2],tot;//第 i 个点在第 k 层时,i 0/1 的编号
int dist[N*10*2],now[N*10*2];
bool judge(){
    bitset<N> vis;
    queue<pair<int,int> > q;q.emplace(s,1);vis[s] = true;
    while(q.size()){
        int x = q.front().first,dis = q.front().second;q.pop();
        for(auto y:e[x]){
            if(vis[y]) continue;
            vis[y] = true;
            int dist = dis + 1;
            q.emplace(y,dist);
            if(y == t){
                if(dist < k) return false;
                else return true;
            }
        }
    }
    return false;
}
bool bfs(int s = s){
    memset(dist,0,sizeof dist);
    queue<int> q;q.push(s);dist[s] = 1;now[s] = g.h[s];
    while(q.size()){
        int x = q.front();q.pop();
        repe(i,x,y,g){
            ll w = g.e[i].w;
            if(dist[y] || !w) continue;
            q.push(y);now[y] = g.h[y];
            dist[y] = dist[x] + 1;
            if(y == t) return true;
        }
    }
    return false;
}
ll dinic(int x,ll flow){
    if(x == t) return flow;
    ll res = 0;
    for(int i = now[x]; i;i = g.e[i].n){
        int y = g.e[i].to;
        ll w = g.e[i].w;
        if(!w) continue;
        if(dist[y] != dist[x] + 1) continue;
        ll k = dinic(y,min(flow,w));
        if(!k) dist[y] = 0;
        g.e[i].w -= k;g.e[i^1].w += k;
        res += k,flow -= k;
    }
    return res;
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cin>>n>>m>>k>>s>>t;rep(i,1,n,1) cin>>c[i];
    rep(k,1,6,1) rep(i,1,n,1) rep(j,0,1,1) mp[i][k][j] = ++tot;
    rep(test,1,m,1){
        int u,v;cin>>u>>v;
        if(pd[u][v]) continue;pd[u][v] = true;
        e[u].emplace_back(v);
        rep(i,1,k,1)
            g.add(mp[u][i][1],mp[v][i][0],1e18),
            g.add(mp[v][i][0],mp[u][i][1],0);
    }
    if(!judge()) cout<<-1,exit(0);
    rep(j,1,k,1) rep(i,1,n,1)
        g.add(mp[i][j][0],mp[i][j][1],c[i]),g.add(mp[i][j][1],mp[i][j][0],0);
    rep(j,1,k-1,1) rep(i,1,n,1)
        g.add(mp[i][j][0],mp[i][j+1][1],1e18),
        g.add(mp[i][j+1][1],mp[i][j][0],0);
    rep(i,1,k-1,1) 
        g.add(mp[t][i][1],mp[t][k][1],1e18),g.add(mp[t][k][1],mp[t][i][1],0);
    s = mp[s][1][0];t = mp[t][k][1];
    ll ans = 0;
    while(bfs()){
        ll res = dinic(s,9e18);
        if(!res) break;
        ans += res;
    }
    cout<<ans<<'\n';
}

circles

(挂题解的话)
image

这真是送分题吗?

题意简述

现在有一张\(2n\)个点的二分图,左边\(n\)个点,右边\(n\)个点。其中左边第\(i\)个点与右边\(1\sim a_i\)个点有边。

你需要求出图中总共有多少个不同的简单环。简单环在这里指一个点只被经过至多一次的环。

答案对\(10^9+7\)取模。

solution

先将所有\(a_i\)排序。

对于一个环而言,如果只保留环中\([1,x]\)的点,那么这个环就被划分成若干条链。自然地,设\(f_{i,j}\)表示只保留\([1,i]\)范围内的点,形成\(j\)条链的方案数。

有初始状态\(f_{0,0} = 1\),考虑转移,就是加入点\(i\)会有什么影响。

\(i\)为右部点,它要么会单独成链,要么和前面的一个链连在一起,即\(f_{i,j}=f_{i-1,j}+f_{i-1,j-1}\)

\(i\)为左部点,那么它要么连上一条链,要么将两条链合并成一条,即\(f_{i,j}=f_{i-1,j}+j\times (j-1)\times f_{i,j+1}\)

\(i\)和之前的一条链接起来成为一个环,将\(f_{i-1,1}\)累加进答案。

由于要剔除二元环,所以答案减去\(\sum\limits_{i=1}^na_i\)

链是有序的,会将一个环计算两次,所以答案除以二。

code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    auto I = freopen("in.in","r",stdin),O = freopen("out.out","w",stdout);
#else
    // auto I = stdin,O = stdout;
    auto I = freopen("circles.in","r",stdin),O = freopen("circles.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = long double;using ldb = long double;
const int N = 5010,mod = 1e9 + 7;
int n,a[N],f[N];
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    int ans = 0;
    cin>>n;rep(i,1,n,1) cin>>a[i],ans -= a[i];sort(a+1,a+1+n);
    f[0] = 1;
    rep(i,1,n,1){
        rep(j,a[i-1]+1,a[i],1) drep(k,j,1,1) f[k] = (f[k] + f[k-1])%mod;
        ans = (ans + f[1])%mod;
        rep(j,1,a[i],1) f[j-1] = (f[j-1] + 1ll*j*(j-1)*f[j]%mod)%mod;
    }
    cout<<1ll*ans*((mod+1)/2)%mod;
}
posted @ 2024-12-28 19:18  CuFeO4  阅读(33)  评论(3)    收藏  举报