- P3386 【模板】二分圖最大匹配
- P2756 飛行員配對方案問題
- P1129 [ZJOI2007]矩陣遊戲
- P1559 運動員最佳匹配問題
- P2423 [HEOI2012]朋友圈
- P2764 最小路徑覆蓋問題
- P2825 [HEOI2016/TJOI2016]遊戲
- P3033 [USACO11NOV]Cow Steeplechase G
- P3731 [HAOI2017]新型城市化
- P4014 分配問題
- P4617 [COCI2017-2018#5] Planinarenje
1.P3386 【模板】二分圖最大匹配
匈牙利算法, 常數小,複雜度沒有網絡流優秀,但是其實小數據跑的更快
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e3+5;
int n,m,k;
vector<int>e[maxn];
int match[maxn],vis[maxn];
bool dfs(int u){
int len=e[u].size();
for(int i=0;i<len;i++){
int v=e[u][i];
if(vis[v])continue;
vis[v]=1;
if(!match[v]||dfs(match[v])){
match[v]=u;
match[u]=v;
return true;
}
}
return false;
}
int xyl(){
int ans=0;
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(dfs(i))ans++;
}
return ans;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=k;i++){
int a,b;
scanf("%d%d",&a,&b);
if(a>n||b>m)continue;
e[a].push_back(n+b);
e[n+b].push_back(a);
}
printf("%d",xyl());
return 0;
}
2.P2756 飛行員配對方案問題
裸的匈牙利,因爲自帶記錄匹配,直接輸出即可
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=2e3+5;
int n,m,k;
int e[maxn][maxn];
int match[maxn],vis[maxn];
bool dfs(int u){
for(int v=m+1;v<=n;v++){
if(vis[v]||!e[u][v])continue;
vis[v]=1;
if(!match[v]||dfs(match[v])){// 如果v沒有匹配,或者v的匹配找到了新的匹配
match[v]=u;
match[u]=v; // 更新匹配信息
return true;
}
}
return false;
}
int xyl(){
int ans=0;
memset(match,0,sizeof(match));
for(int i=1;i<=n;i++){// 對每一個點嘗試匹配
memset(vis,0,sizeof(vis));
if(dfs(i))ans++;
}
return ans;
}
inline void init(){
memset(e,0,sizeof(e));
}
int main(){
scanf("%d%d",&m,&n);
int u,v;
while(scanf("%d%d",&u,&v)){
if(u==-1&&v==-1)break;
e[u][v]=1;
e[v][u]=1;
}
printf("%d\n",xyl());
for(int i=1;i<=m;i++){
if(match[i]){
printf("%d %d\n",i,match[i]);
}
}
return 0;
}
3.P1129 [ZJOI2007]矩陣遊戲
行列匹配數是否==行數,可以只考慮一種移法,只移動行或者列,然後只要每個行列匹配成功,更換他們的位置並不會影響匹配數
#include<bits/stdc++.h>
using namespace std;
const int maxn=205;
int n,m,k;
int e[maxn][maxn];
int match[maxn],vis[maxn],a[maxn][maxn];
bool dfs(int u){
for(int v=1;v<=n;v++){
if(vis[v]||!e[u][v])continue;
vis[v]=1;
if(!match[v]||dfs(match[v])){
match[v]=u;
return true;
}
}
return false;
}
int xyl(){
int ans=0;
memset(match,0,sizeof match);
for(int i=1;i<=n;i++){
memset(vis,0,sizeof vis);
if(dfs(i))ans++;
}
return ans;
}
inline void init(){
memset(e,0,sizeof e);
}
int main(){
int tt;
cin>>tt;
while(tt--){
cin>>n;
init();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&e[i][j]);
}
}
int ans=xyl();
if(ans==n)printf("Yes\n");
else printf("No\n");
}
}
4.P1559 運動員最佳匹配問題
裸的KM
#include<bits/stdc++.h>
using namespace std;
#define int long long
const long long maxn=1005,inf=9e18;
int n,m,mp[maxn][maxn],matched[maxn],p[105][105],q[105][105];
int slack[maxn],ex[maxn],ey[maxn],pre[maxn];
int visx[maxn],visy[maxn];
void match(int u){
int x,y=0,yy=0,d;
memset(pre,0,sizeof(pre));
for(int i=1;i<=n;i++)slack[i]=inf;
matched[y]=u;
while(1){
x=matched[y];
d=inf;
visy[y]=1;
for(int i=1;i<=n;i++){
if(visy[i])continue;
if(slack[i]>ex[x]+ey[i]-mp[x][i]){
slack[i]=ex[x]+ey[i]-mp[x][i];
pre[i]=y;
}
if(slack[i]<d){
d=slack[i];
yy=i;
}
}
for(int i=0;i<=n;i++){
if(visy[i])ex[matched[i]]-=d,ey[i]+=d;
else slack[i]-=d;
}
y=yy;
if(matched[y]==-1)break;
}
while(y){
matched[y]=matched[pre[y]];
y=pre[y];
}
}
int km(){
memset(matched,-1,sizeof(matched));
memset(ex,0,sizeof(ex));
memset(ey,0,sizeof(ey));
for(int i=1;i<=n;i++){
memset(visy,0,sizeof(visy));
match(i);
}
int ans=0;
for(int i=1;i<=n;i++){
if(matched[i]!=-1)ans+=mp[matched[i]][i];
}
return ans;
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
mp[i][j]=-inf;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>p[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>q[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
mp[i][j]=p[i][j]*q[j][i];
}
}
// for(int i=1;i<=m;i++)
// {
// int u,v,w;
// scanf("%lld%lld%lld",&u,&v,&w);
// mp[u][v]=w;
// }
printf("%lld\n",km());
for(int i=1;i<=n;i++){
// printf("%lld ",matched[i]);
}
return 0;
}
5.P2423 [HEOI2012]朋友圈
分析一下AB國的情況:
A國只能一奇一偶,所以這個國的團中最多隻有1個或者2個人
B國奇偶相同的在同一個團
所以可以枚舉a國的每一個團,1或者2,跟b國進行匹配
最大團=補圖最大獨立集=總點數補圖最大匹配數
不能無腦memset而要用時間戳進行優化匈牙利,否則會T
(最後這題寫着多組輸入但是其實並沒有,數據有鍋)
#include<bits/stdc++.h>
using namespace std;
const int maxn=3005;
int n,m,k,cnt,ans,num,t,flag[maxn];
int e[maxn][maxn],head[maxn],a[maxn],b[maxn];
int match[maxn],vis[maxn];
struct edge{
int v,nex;
}ee[2000*2000+5];
inline void add(int u,int v){
ee[++cnt].v=v;
ee[cnt].nex=head[u];
head[u]=cnt;
}
bool dfs(int u){
for(int i=head[u];i;i=ee[i].nex){
int v=ee[i].v;
if(vis[v]!=num&&flag[v]==t){
vis[v]=num;
if(!match[v]||dfs(match[v])){
match[v]=u;
return true;
}
}
}
return false;
}
int main(){
int tt,A,B;
cin>>tt;
while(tt--){
ans=0;
cin>>A>>B>>m;
for(int i=1;i<=A;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=B;i++){
scanf("%d",&b[i]);
}
for(int i=1;i<=B;i++){
if(b[i]&1){
for(int j=1;j<=B;j++){
if(!(b[j]&1)&&!((__builtin_popcount((b[i]|b[j])))&1)){
add(i,j);
}
}
}
}
int u,v;
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
e[u][v+A]=1;
e[v+A][u]=1;
}
for(int i=1;i<=B;i++){
if((b[i]&1)){
num++;
if(dfs(i))ans++;
}
}
ans=B-ans;
for(int i=1;i<=A;i++){
t++;
int sum=0,now=0;
memset(match,0,sizeof match);
for(int j=1;j<=B;j++){
if(e[i][j+A]){
flag[j]=t;
now++;
}
}
for(int j=1;j<=B;j++){
if(flag[j]==t&&(b[j]&1)){
num++;
if(dfs(j))sum++;
}
}
ans=max(ans,now-sum+1);
}
for(int i=1;i<=A;i++){
for(int j=i+1;j<=A;j++){
if((a[i]^a[j])&1){
t++;
int sum=0,now=0;
memset(match,0,sizeof match);
for(int k=1;k<=B;k++){
if(e[i][A+k]&&e[j][A+k]){
flag[k]=t;
now++;
}
}
for(int k=1;k<=B;k++){
if(flag[k]==t&&(b[k]&1)){
num++;
if(dfs(k))sum++;
}
}
ans=max(ans,now-sum+2);
}
}
}
cout<<ans<<endl;
}
return 0;
}
6.P2764 最小路徑覆蓋問題
給定有向圖 。設 是 的一個簡單路(頂點不相交)的集合。如果 中每個定點恰好在的一條路上,則稱 是 的一個路徑覆蓋。中路徑可以從 的任何一個定點開始,長度也是任意的,特別地,可以爲 。 的最小路徑覆蓋是 所含路徑條數最少的路徑覆蓋。設計一個有效算法求一個 GAP (有向無環圖) 的最小路徑覆蓋。
提示:設 ,構造網絡 如下:
每條邊的容量均爲 ,求網絡 的 最大流。
題目直接給出了提示,可是我太蒻了沒有看懂…
原圖是這樣子的:
要我們求最小覆蓋路徑,我們可以先把圖看成沒有邊,此時覆蓋路徑數爲11,通過手玩可以發現,每當我們連上一條邊之後,這個路徑數就會減少1,而每一條路徑上,一個點只能被另外一個點連上,這就符合匹配的原理,我們由此構建出二分圖:
可以看出最小路徑覆蓋數=總點數-最大流,因爲最大流即爲匹配數,被匹配上的點就不需要做出發點
題目要求我們輸出路徑,在dfs找增廣路的時候記錄下每一個點的就好
#include<bits/stdc++.h>
using namespace std;
const int inf =0x3f3f3f3f,maxn=1e5+5,maxm=4e5+5;
int cur[maxn],head[maxn],dep[maxn],to[maxn],flag[maxn];
int cnt=1,n,m,s,t;
struct edge{
int v,w,nex;
}e[maxm*2];
inline void add_edge(int u,int v,int w){
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].w=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
bool bfs(){
memset(dep,0,sizeof(dep));
for(int i=0;i<=2*n+5;i++){//如果要拆點或者其他的一定記得把範圍開大點!!!
cur[i]=head[i];
}
dep[s]=1;
queue<int>q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(!dep[v]&&e[i].w){
dep[v]=dep[u]+1;
q.push(v);
if(v==t)return true;
}
}
}
return false;
}
int dfs(int u,int now){
if(u==t||now==0){
return now;
}
int flow=0,rlow=0;
for(int i=cur[u];i;i=e[i].nex){
int v=e[i].v;
if(e[i].w&&dep[v]==dep[u]+1){
if(rlow=dfs(v,min(now,e[i].w))){
flow+=rlow;
now-=rlow;
e[i].w-=rlow;
e[i^1].w+=rlow;
to[u]=v;
if(u!=s)flag[v-n]=1;
if(now==0)return flow;
}
}
}
if(!flow)dep[u]=-1;
return flow;
}
int dinic(){
int maxflow=0;
while(bfs()){
maxflow+=dfs(s,inf);
}
return maxflow;
}
int main(){
scanf("%d%d",&n,&m);
int u,v,w;
s=0,t=2*n+1;
for(int i=1;i<=n;i++){
add_edge(s,s+i,1);
add_edge(s+n+i,t,1);
}
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
add_edge(s+u,s+n+v,1);
}
int ans=dinic();
for(int i=1;i<=n;i++){
if(!flag[i]){
int now=i;
printf("%d ",now);
while(to[now]&&to[now]!=t){
printf("%d ",to[now]-n);
now=to[now]-n;
}
cout<<endl;
}
}
printf("%d",n-ans);
return 0;
}
7.P2825 [HEOI2016/TJOI2016]遊戲
每隔一個牆就,對行列進行匹配求出最大匹配數即可
#include<bits/stdc++.h>
using namespace std;
const int maxn=2005+5;
int xcnt=1,ycnt=1;
int n,m,k;
int e[maxn][maxn],x[maxn][maxn],y[maxn][maxn];
int match[maxn],vis[maxn];
char a[maxn][maxn];
bool dfs(int u){
for(int v=1;v<=xcnt;v++){
if(vis[v]||!e[u][v])continue;
vis[v]=1;
if(!match[v]||dfs(match[v])){
match[v]=u;
return true;
}
}
return false;
}
int xyl(){
int ans=0;
memset(match,0,sizeof match);
for(int i=1;i<=xcnt;i++){
memset(vis,0,sizeof vis);
if(dfs(i))ans++;
}
return ans;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]=='*'){
x[i][j]=xcnt;
}
else if(a[i][j]=='#')xcnt++;
}
xcnt++;
}
for(int j=1;j<=m;j++){
for(int i=1;i<=n;i++){
if(a[i][j]=='*'){
y[i][j]=ycnt;
}
else if(a[i][j]=='#')ycnt++;
}
ycnt++;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int u=x[i][j];
int v=y[i][j];
if(a[i][j]=='*'){
e[u][v]=1;
}
}
}
printf("%d",xyl());
return 0;
}
8.P3033 [USACO11NOV]Cow Steeplechase G
剛開始讀錯了題,後面突然發現是要求最大團,秒殺之
再寫一遍:最大團=補圖最大獨立集=總點數-補圖最大匹配數
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int n,m,k,xcnt,ycnt;
int e[maxn][maxn];
int match[maxn],vis[maxn],a[maxn][maxn];
struct xd{
int x,y1,y2;
}xd[maxn];
struct yd{
int x1,x2,y;
}yd[maxn];
bool dfs(int u){
for(int v=1;v<=n;v++){
if(vis[v]||!e[u][v])continue;
vis[v]=1;
if(!match[v]||dfs(match[v])){
match[v]=u;
return true;
}
}
return false;
}
int xyl(){
int ans=0;
memset(match,0,sizeof match);
for(int i=1;i<=n;i++){
memset(vis,0,sizeof vis);
if(dfs(i))ans++;
}
return ans;
}
int main(){
cin>>n;
int x1,x2,y1,y2;
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
if(x1==x2){
xd[++xcnt].x=x1;
xd[xcnt].y1=y1;
xd[xcnt].y2=y2;
}
else if(y1==y2){
yd[++ycnt].y=y1;
yd[ycnt].x1=x1;
yd[ycnt].x2=x2;
}
}
for(int i=1;i<=xcnt;i++){
int x=xd[i].x;
int y1=xd[i].y1;
int y2=xd[i].y2;
if(y1>y2)swap(y1,y2);
for(int j=1;j<=ycnt;j++){
int y=yd[j].y;
int x1=yd[j].x1;
int x2=yd[j].x2;
if(x1>x2)swap(x1,x2);
if(x1<=x&&x2>=x&&y1<=y&&y2>=y){
e[i][j]=1;
}
// e[i][j]=1;
}
}
printf("%d",max(xcnt,max(ycnt,n-xyl())));
return 0;
}
9.P3731 [HAOI2017]新型城市化
這個題做的我晚上腦子快要爆炸…
一看數據範圍很明顯是要的做法纔行
半天也沒看明白題目的真意…
題目給出了反圖
題目問加上哪些邊可以讓最大團變大
而 最大團=總點數-補圖最大匹配數
所以補圖的最大匹配數越小,最大團就越大,也就是補圖最大獨立集越大,也就是刪掉哪些邊能讓圖的最大獨立集增大
luogu上的dalao的分析:
考慮如下定理:若一條邊一定在最大匹配中,則在最終的殘量網絡中,這條邊一定滿流,且這條邊的兩個頂點一定不在同一個強連通分量中。
證明也很簡單:首先滿流的要求是很顯然的,其次,如果這兩個點在同一個強連通分量中,那麼一定有一個環經過這條邊,沿着環增廣一下,網絡仍然滿足流量限制,但是這條邊就不滿流了,於是就得到了一組新的最大匹配。
於是我們終於讀懂題目了,就是求二分圖匹配的必須邊
直接在殘量網絡裏跑,就是如果這條邊沒有滿流就連上
對於一條邊
如果是一條匹配邊或者,在同一個強聯通分量裏,那麼這就是一條最大匹配的可行邊
如果是一條匹配邊並且,不在同一個強連通分量裏,那麼這就是一條必須邊
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e4+5;
const int maxm=150000+5,inf=0x3f3f3f3f;
int head[maxn],n,m,cnt=1,dep[maxn],vis[maxn],cur[maxn];
int s,t,co[maxn];
struct edge{
int v,w,nex;
}e[maxm*2];
vector<int>ee[maxn];
inline void add_edge(int u,int v,int w){
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].w=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
bool bfs(){
memset(dep,0,sizeof(dep));
for(int i=s;i<=t;i++){//如果要拆點或者其他的一定記得把範圍開大點!!!
cur[i]=head[i];
}
dep[s]=1;
queue<int>q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(!dep[v]&&e[i].w){
dep[v]=dep[u]+1;
q.push(v);
if(v==t)return true;
}
}
}
return false;
}
int dfs(int u,int now){
if(u==t||now==0){
return now;
}
int flow=0,rlow=0;
for(int i=cur[u];i;i=e[i].nex){
int v=e[i].v;
if(e[i].w&&dep[v]==dep[u]+1){
if(rlow=dfs(v,min(now,e[i].w))){
flow+=rlow;
now-=rlow;
e[i].w-=rlow;
e[i^1].w+=rlow;
if(now==0)return flow;
}
}
}
if(!flow)dep[u]=-1;
return flow;
}
int dinic(){
int maxflow=0;
while(bfs()){
maxflow+=dfs(s,inf);
}
return maxflow;
}
namespace Tarjan {
std::vector<int> v[maxn];
int dfn[maxn],low[maxn],col[maxn],st[maxn],f[maxn];
int top,p,cnt,mid,tot;
std::pair<int,int> ans[150005];
void tarjan(int x) {
dfn[x]=low[x]=++cnt;
f[x]=1,st[++top]=x;
for(int i=0;i<v[x].size();++i) {
int j=v[x][i];
if(!dfn[j]) tarjan(j),low[x]=min(low[x],low[j]);
else if(f[j]) low[x]=min(low[x],dfn[j]);
}
if(dfn[x]==low[x]) {
++p;
do {
mid=st[top--];
f[mid]=0;
col[mid]=p;
}while(mid!=x);
}
}
void solve() {
for(int i=s;i<=t;i++)
for(int j=head[i];j;j=e[j].nex)
if(e[j].w) v[i].push_back(e[j].v);
for(int i=s;i<=t;i++) if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++) {
if(co[i]) continue;
for(int j=head[i];j;j=e[j].nex)
if(col[i]!=col[e[j].v]&&!e[j].w&&e[j].v!=s) {
int xx=min(i,e[j].v);
int yy=max(i,e[j].v);
ans[++tot]=make_pair(xx,yy);
}
}
std::sort(ans+1,ans+tot+1);
printf("%d\n",tot);
for(int i=1;i<=tot;i++) printf("%d %d\n",ans[i].first,ans[i].second);
}
}
void rs(int u,int c){
co[u]=c;
for(int i=0;i<ee[u].size();i++) {
int v=ee[u][i];
if(co[v]!=2) continue;
rs(v,c^1);
}
}
int main(){
cin>>n>>m;
int x,y;
t=n+1;
s=0;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
ee[x].push_back(y);
ee[y].push_back(x);
}
for(int i=1;i<=n;i++){
co[i]=2;
}
for(int i=1;i<=n;i++){
if(co[i]==2)rs(i,0);
}
for(int i=1;i<=n;i++){
if(!co[i])add_edge(s,i,1);
else add_edge(i,t,1);
}
for(int i=1;i<=n;i++){
if(co[i])continue;
for(int j=0;j<ee[i].size();j++){
add_edge(i,ee[i][j],1);
}
}
int an=dinic();
Tarjan::solve();
return 0;
}
10.P4014 分配問題
正負KM
#include<bits/stdc++.h>
using namespace std;
#define int long long
const long long maxn=1005,inf=1e18;
int n,m,mp[maxn][maxn],matched[maxn];
int slack[maxn],ex[maxn],ey[maxn],pre[maxn];
int visx[maxn],visy[maxn];
inline void init(){
memset(slack,0,sizeof slack);
memset(ex,0,sizeof ex);
memset(ey,0,sizeof ey);
memset(pre,0,sizeof pre);
memset(matched,0,sizeof matched);
memset(visx,0,sizeof visx);
memset(visy,0,sizeof visy);
}
void match(int u){
int x,y=0,yy=0,d;
memset(pre,0,sizeof(pre));
for(int i=1;i<=n;i++)slack[i]=inf;
matched[y]=u;
while(1){
x=matched[y];
d=inf;
visy[y]=1;
for(int i=1;i<=n;i++){
if(visy[i])continue;
if(slack[i]>ex[x]+ey[i]-mp[x][i]){
slack[i]=ex[x]+ey[i]-mp[x][i];
pre[i]=y;
}
if(slack[i]<d){
d=slack[i];
yy=i;
}
}
for(int i=0;i<=n;i++){
if(visy[i])ex[matched[i]]-=d,ey[i]+=d;
else slack[i]-=d;
}
y=yy;
if(matched[y]==-1)break;
}
while(y){
matched[y]=matched[pre[y]];
y=pre[y];
}
}
int km(){
memset(matched,-1,sizeof(matched));
memset(ex,0,sizeof(ex));
memset(ey,0,sizeof(ey));
for(int i=1;i<=n;i++){
memset(visy,0,sizeof(visy));
match(i);
}
int ans=0;
for(int i=1;i<=n;i++){
if(matched[i]!=-1)ans+=mp[matched[i]][i];
}
return ans;
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
mp[i][j]=-inf;
// for(int i=1;i<=m;i++){
// int u,v,w;
// scanf("%lld%lld%lld",&u,&v,&w);
// mp[u][v]=w;
// }
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%lld",&mp[i][j]);
}
}
int mm=km();
init();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
mp[i][j]=-mp[i][j];
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(mp[i][j]==-inf)mp[i][j]=inf;
printf("%lld\n%lld",-km(),mm);
return 0;
}
11.P4617 [COCI2017-2018#5] Planinarenje
黑題,咕咕咕待補