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
(挂题解的话)
这真是送分题吗?
题意简述
现在有一张\(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;
}