The Benelux Algorithm Programming Contest (BAPC) is an algorithm competition that brings together about 50 teams from top universities in Luxembourg, Belgium and the Netherlands.

In this competition, the team that has passed the most questions will be recommended to participate in the North Western European Regional contest, and the winning team in NERC can participate in ICPC World Final, that is, this is a regional trial.

Today, let's look at the topic of 2018 Benelux Algorithm Programming Contest (BAPC 18).

## Title Link

https://2018.bapc.eu/ Or http://opentrains.snarknews.info/~ejudge/team.cgi?contest_id=006313

## Problem A

Very simple check-in question.

#include <bits/stdc++.h> using namespace std; const int MAXN = 1e5+5; int a[MAXN], n, x; int main(){ scanf("%d%d", &n, &x); for (int i=0; i<n; ++i) scanf("%d", &a[i]); int ans = 1; sort(a, a+n); for (int i=1; i<n; ++i) { if (a[i]+a[i-1] > x) { break; } else ans = max(ans, i+1); } cout << ans << endl; return 0; }

## Problem B

There are some people in the office who have birthdays. Now you want to arrange your birthday on the day that is the longest from the last birthday. If you have more solutions, try to be as close as possible to the last October 27, so as to find out which day will be arranged in the end.

Problem solution: get rid of the habit of looking for the optimal solution. It's just a matter of violence.

#include<iostream> #include<cstdio> using namespace std; const int maxn=400; const int days[14]={0,31,28,31,30,31,30,31,31,30,31,30,31,0}; int bef[14]; int trans(int mm,int dd){ return bef[mm]+dd; } void _trans(int day,int &mm,int &dd){ for (mm=1;mm<=12;mm++) if (bef[mm+1]>=day){ dd=day-bef[mm]; return; } } char s[20]; bool exi[maxn]; int cal(int n){ int ans=0; while (!exi[n]){ n--; if (n==0) n=365; ans++; } return ans; } int main(){ int i,n,m,d; int ans,day,cur; bef[1]=0; for (i=1;i<=13;i++) bef[i]=bef[i-1]+days[i-1]; scanf("%d",&n); for (i=0;i<n;i++){ scanf("%s %d-%d",s,&m,&d); exi[trans(m,d)]=true; } ans=-1; cur=-1; day=trans(10,28); for (i=day;i!=day-1;i=i%365+1){ if (cal(i)>ans){ ans=cal(i); cur=i; } } if (cal(day-1)>ans){ ans=cal(day-1); cur=day-1; } _trans(cur,m,d); printf("%02d-%02d\n",m,d); return 0; }

## Problem C

Construct a cube whose three sides are integers, so that the volume is equal to the given value and the surface area is the smallest.

Solution: the length of the first two edges can be enumerated in the root range.

#include<iostream> #include<cstdio> using namespace std; const int inf=1e9; int main(){ int v; int a,b,c; int ans; scanf("%d",&v); ans=inf; for (a=1;a*a<=v;++a) if (v%a==0) for (b=1;b*b<=v/a;++b) if (v/a%b==0){ c=v/a/b; ans=min(ans,2*(a*b+b*c+c*a)); } printf("%d\n",ans); return 0; }

## Problem D

In an undirected graph, two people drive and play. One assumes that he is currently in A and one assumes that he is in B. One person is right. Tell you whether you can see the tower at each point. The exit degree of each point is 2. Ask how many steps to take at least to judge which person is right (if one is inconsistent with the facts, it means the other is right), or there is no solution.

Problem solving: good thinking. bfs yes

n^2Status, end up in TLE. The most important thing is that the positive solution is bfs optimization. If (a,b) is in the queue and (b,c) is in the queue, then (a,c) does not need to search and determine that it has no solution. Why? If (a,b) has no solution and (b,c) has no solution, then obviously (a,c) has no solution, because they are the same, then the no solution is regarded as an equivalence relationship. If (a,c) has a solution, it means that at least one of (a,b)(b,c) also has a solution, then the solution can also be found. Why is it better than (a,c)? Because if (a,c) has a common prefix that goes everywhere, then (a,b) is obviously better if the prefix of (a,b) is short. If the prefix of (a,b) is long, the answer to (a,c) will be the same as (b,c) (in fact, the principle that no solution is equivalent under a finite number of steps is also used here). After using and searching the set, we found that the state changed to

O(n)## Problem E

Given a sequence, ask which of its different permutations are completely out of order. The definition of complete out of order is that each position is out of order. A position that is out of order is defined as having a larger point on the left or a smaller point on the right.

Solution: two dimensional dp.

We first sort the sequence and classify it according to element values. set up

cnt_iRepresentative section

iThe number of elements,

tot_iBefore representative

iThe number of elements,

dp[i][j]Represents the smallest consideration

iClass element, before

jBit out of order and

j+1The total number of out of order bits. Initially, only

dp[0][0]=0. take

dp[i+1][j]It is divided into three categories according to scope:

first kind

0 \le j < tot_i. In this case, paragraph

i+1The first element should be inserted in the second element

jAfter the first element.

Type II

j=tot_i. In this case, paragraph

i+1All elements are at the end.

Type III tot_ i<j \le="" tot_ {I + 1} I + 1 = "" where elements appear for the first time, the front part must be out of order, and there is = "" tot at the end_ {I + 1}-j = "" consecutive element = "" I = ""<= "" p="">

Assume common ownership

mspecies

nA number and calculate the dp value for each case,

dp[m][n]That's the answer.

## Problem F

Title meaning; have

nThere are items to choose from. Each item costs

c_iAfter that, you can get it every day

p_iFind the minimum number of days to obtain the value

M.

Solution: in two days, all items that can be earned are included in the bag, and

MJust compare the size.

#include <bits/stdc++.h> #define LL long long using namespace std; int n; LL m; struct data { LL c,p; }a[100010]; bool check(LL x) { LL ans=0; for (int i=1;i<=n;i++) if (a[i].p*x>a[i].c) { ans+=a[i].p*x-a[i].c; if (ans>=m) return true; } return ans>=m; } int main() { scanf("%d%lld",&n,&m); for (int i=1;i<=n;i++) scanf("%lld%lld",&a[i].p,&a[i].c); LL l=1,r=2000000010LL,mid,res; while(l<=r) { mid=l+r>>1; if (check(mid)) res=mid,r=mid-1;else l=mid+1; } printf("%lld\n",res); return 0; }

## Problem G

There are three teams sitting on the ring. People in the same team are required to sit together to minimize the number of people exchanged.

First of all, if the final state is determined, the minimum number of people to be exchanged is the number different from the final state position.

So it is to enumerate the final state, enumerate the relative relationship of the three teams, and use violence, one

6Two cases;

Enumerate all the team relationships in each case, move according to the ring, use six pointers to represent the beginning and end of each team, and use a sliding window

O(1)Just maintain it.

#include <bits/stdc++.h> #define LL long long using namespace std; int n,A,B,C,ans; char s[100010]; void solve(int la,int ra,int lb,int rb,int lc,int rc,int suma,int sumb,int sumc) { for (int i=1;i<=n;i++) { if (s[la]!='A') suma--;la++;if (la>n) la-=n; ra++;if (ra>n) ra-=n;if (s[ra]!='A') suma++; if (s[lb]!='B') sumb--;lb++;if (lb>n) lb-=n; rb++;if (rb>n) rb-=n;if (s[rb]!='B') sumb++; if (s[lc]!='C') sumc--;lc++;if (lc>n) lc-=n; rc++;if (rc>n) rc-=n;if (s[rc]!='C') sumc++; //printf("%d %d %d %d %d %d %d %d %d\n",la,ra,lb,rb,lc,rc,suma,sumb,sumc); ans=min(ans,suma+sumb+sumc); } } void work(int la,int ra,int lb,int rb,int lc,int rc) { int suma=0,sumb=0,sumc=0; for (int i=1;i<=n;i++) if (i>=la&&i<=ra&&s[i]!='A') suma++; else if (i>=lb&&i<=rb&&s[i]!='B') sumb++; else if (i>=lc&&i<=rc&&s[i]!='C') sumc++; //printf("!!!!!!!%d %d %d %d %d %d %d %d %d\n",la,ra,lb,rb,lc,rc,suma,sumb,sumc); ans=min(ans,suma+sumb+sumc); solve(la,ra,lb,rb,lc,rc,suma,sumb,sumc); //cout<<ans<<endl; } int main() { scanf("%d",&n); scanf("%s",s+1); A=0,B=0,C=0; for (

## Problem H

Meaning: two people operate a piece in turn and walk on the directed graph. The first person wants to walk as long as possible and the second person wants to walk as short as possible. Find the length of the last walk and judge it as infinite.

Solution: we consider splitting each point into two. The first point represents the first person walking when the chess piece is here, and the second point represents the second person walking.

For the first type of point, its path length can be determined only when the path length from all the second type points it can reach to the end point is determined. For the second type of point, its path length can be determined as long as its minimum possible path length is the smallest of all currently undetermined second type points.

We know that the path length of the two points removed at the end point is the same

0Therefore, the answer is extended from the end point according to this law. If it can be extended to the first type of point removed from the starting point, the answer will be output. If it cannot be determined, the distance is infinite in this case.

#include<iostream> #include<cstdio> #include<set> using namespace std; const int maxn=2e5+5,maxm=4e5+5,inf=2e9+5; int head1[maxn],to1[maxm],wei1[maxm],next1[maxm],tot1; int head2[maxn],to2[maxm],wei2[maxm],next2[maxm],tot2; int du[maxn],dp[maxn]; int que[maxn]; struct node{ int id,val; node(int _i=0,int _v=0){ id=_i; val=_v; } }; bool operator < (node a,node b){ if (a.val!=b.val) return a.val<b.val; return a.id<b.id; } void add1(int u,int v,int w){ tot1++; next1[tot1]=head1[u]; head1[u]=tot1; to1[tot1]=v; wei1[tot1]=w; } void add2(int u,int v,int w){ tot2++; next2[tot2]=head2[u]; head2[u]=tot2; to2[tot2]=v; wei2[tot2]=w; } set<node> st; int main(){ int i,n,m; int u,v,w,ed,s,t; int l,r; int ans; scanf("%d%d%d%d",&n,&m,&s,&t); if (s==t){ puts("0"); return 0; } tot1=tot2=0; for (i=0;i<m;i++){ scanf("%d%d%d",&u,&v,&w); add1(u,v,w); du[u]++; add2(v,u,w); } for (i=0;i<n;i++) dp[i]=inf; dp[t]=0; for (ed=head2[t];ed;ed=next2[ed]){ // du[to2[ed]]--; dp[to2[ed]]=wei2[ed]; } for (i=0;i<n;i++) st.insert(node(i,dp[i])); while (!st.empty()){ u=st.begin()->id; st.erase(st.begin()); for (ed=head2[u];ed;ed=next2[ed]){ v=to2[ed]; du[v]--; if (!du[v]){ ans=0; for (i=head1[v];i;i=next1[i]){ ans=max(ans,dp[to1[i]]+wei1[i]); } if (v==s){ if (ans<=inf-3) printf("%d\n",ans); else puts("infinity"); return 0; } for (i=head2[v];i;i=next2[i]) if (dp[to2[i]]>ans+wei2[i]){ st.erase(node(to2[i],dp[to2[i]])); dp[to2[i]]=ans+wei2[i]; st.insert(node(to2[i],dp[to2[i]])); } } } } puts("infinity"); return 0; }

## Problem I

Meaning:

nPoints

mThe graph with edges has

sA shelter requires all people to enter one of the shelters, and the person who takes the longest time takes the least time.

Question solution: the answer can be divided into two parts, so it can be obtained according to the preprocessing distance

nOne point can reach those shelters respectively, and the shape pressure is carried out according to the different shelters that can be reached, because only

10A sanctuary, so at most

1024Two states.

Due to the capacity of refuge, there is a total number of people in each state. It is required that all people can reach at least one shelter. Consider the network flow mapping.

For each state, limiting the flow in represents the maximum number of people in each state, and limiting the flow out of each shelter represents the maximum capacity of each shelter.

If the maximum flow = the total number of people, then obviously this time is feasible, and we can consider making the time smaller.

#include <bits/stdc++.h> #define INF 0x3fffffffffffffff #define LL long long using namespace std; struct E { int to;LL cp; E(int to,LL cp):to(to),cp(cp){} }; struct Dinic { static const int M=1E5*5; int m; vector<E> edges; vector<int> G[M]; int d[M]; int cur[M]; void init(int n) { for (int i=0;i<=n;i++) G[i].clear(); edges.clear();m=0; } void addedge(int u,int v,LL cap) { //cout<<u<<" "<<v<<" "<<cap<<endl; edges.push_back(E(v,cap)); edges.push_back(E(u,0LL)); G[u].push_back(m++); G[v].push_back(m++); } bool BFS(int s,int t) { memset(d,0,sizeof(d)); queue<int> Q; Q.push(s),d[s]=1; while(!Q.empty()) { int x=Q.front();Q.pop(); int k = G[x].size(); for (int j=0; j<k; ++j) { int &i = G[x][j]; E &e=edges[i]; if (!d[e.to]&&e.cp>0) { d[e.to]=d[x]+1; Q.push(e.to); } } } return d[t]; } LL DFS(int u,LL cp,int s,int t) { if (u==t||!cp) return cp; LL tmp=cp,f; for (int& i=cur[u];i<G[u].size();i++) { E& e=edges[G[u][i]]; if (d[u]+1==d[e.to]) { f=DFS(e.to,min(cp,e.cp),s,t); e.cp-=f; edges[G[u][i]^1].cp+=f; cp-=f; if (!cp) break; } } return tmp-cp; } LL go(int s,int t) { LL flow=0; while(BFS(s,t)) { memset(cur,0,sizeof cur); flow+=DFS(s,INF,s,t); } return flow; } }DC; struct edge { int to; LL cost; edge(int to, LL cost):to(to), cost(cost){} }; const int MAXN = 1e5+5; vector<edge> G[MAXN]; void add(int u, int v, LL w) { G[u].push_back(edge(v, w)); G[v].push_back(edge(u, w)); } struct Dij { LL dist[MAXN]; struct pqNode { int p; LL v; pqNode(int p, LL v):p(p), v(v){} bool operator < (const pqNode &other) const { return v > other.v; } }; priority_queue<pqNode> pq; void dij(int s, int n) { while (!pq.empty()) pq.pop(); for (int i=0; i<=n; ++i) { dist[i] = INF; } pq.push(pqNode(s, 0)); dist[s] = 0; while (!pq.empty()) { pqNode now = pq.top(); pq.pop(); if (now.v > dist[now.p]) continue; int siz = G[now.p].size(); for (int i=0; i<siz; ++i) { edge &e = G[now.p][i]; if (dist[e.to] > dist[now.p] + e.cost) { dist[e.to] = dist[now.p] + e.cost; pq.push(pqNode(e.to, dist[e.to])); } } } } LL getDist(int j) { return dist[j]; } }dij[11]; int n,m,s; LL c[100010],b[5010],sum; struct data { int s;LL c; }a[20]; bool check(LL x) { int z=1; for (int i=1;i<=s;i++) z*=2; for (int i=0;i<=z;i++) b[i]=0; DC.init(z+s+2);int S=z+s+1,T=z+s+2; for (int i=1;i<=n;i++) { int y=0; for (int j=1;j<=s;j++) if (dij[j].getDist(i)<=x) y=y*2+1;else y=y*2; b[y]+=c[i]; } for (int i=0;i<z;i++) { DC.addedge(S,i+1,b[i]); int y=i; for (int j=s;j>=1;j--) { if (y%2==1) DC.addedge(i+1,z+j,INF); y/=2; } } for (int i=1;i<=s;i++) DC.addedge(z+i,T,a[i].c); return DC.go(S,T)>=sum; } int main() { scanf("%d%d%d",&n,&m,&s); sum=0; for (int i=1;i<=n;i++) scanf("%lld",&c[i]),sum+=c[i]; int x,y;LL z; for (int i=1;i<=m;i++) scanf("%d%d%lld",&x,&y,&z),add(x,y,z); for (int i=1;i<=s;i++) scanf("%d%lld",&a[i].s,&a[i].c), dij[i].dij(a[i].s,n); LL l=0,r=1E15,mid,res; while(l<=r) { mid=l+r>>1; if (check(mid)) res=mid,r=mid-1;else l=mid+1; } cout<<res<<endl; return 0; }

## Problem J

Given the length of four sides, find the maximum area of a quadrilateral that can be formed.

After guessing that conclusion 1 must be trapezoid, guess conclusion 2: the relationship between the area of a quadrilateral and the length of one of its diagonals is a unimodal function.

In this case, first enumerate the positions of the four edges, and then find the maximum value of the convex function by the trisection method. After that, it shows that you guessed right.

#include<iostream> #include<cstdio> #include<cmath> using namespace std; const double ee=1e-12; double tri(double a,double b,double c){ double p=(a+b+c)/2; return sqrt(fabs(p*(p-a)*(p-b)*(p-c))); } double split(double a,double b,double c,double d,double l,double r){ if (r-l<ee) return tri(a,b,(l+r)/2)+tri(c,d,(l+r)/2); double m1,m2; m1=(l+r)/2; m2=(m1+r)/2; if (tri(a,b,m1)+tri(c,d,m1)>tri(a,b,m2)+tri(c,d,m2)) return split(a,b,c,d,l,m2); else return split(a,b,c,d,m1,r); } double solve(double a,double b,double c,double d){ double l,r; l=max(fabs(a-b),fabs(c-d)); r=min(a+b,c+d); if (l>=r) return 0.0; return split(a,b,c,d,l,r); } int main(){ double a,b,c,d; double ans; scanf("%lf%lf%lf%lf",&a,&b,&c,&d); ans=0; ans=max(ans,solve(a,b,c,d)); ans=max(ans,solve(a,c,b,d)); ans=max(ans,solve(a,d,b,c)); printf("%.12f\n",ans); return 0; }

## Problem K

How many edges are added to a tree so that the whole tree becomes a doubly connected component

Question solution: it is the original question to ask how many sides to add, but this question needs to construct a solution. In the spirit of hesitation, he will lose. He rushed up blindly and gave it in vain when he ate a penalty. Later, it seemed that the structural solution was also the conclusion of many schools. The points with degree 1 are sorted in dfs order to ensure an interval of n/2 points, and the middle of the odd number is connected to both sides. The reason why edge connection is invalid is that two points are connected under the same subtree and lca is not the root node. If you can match two by two across half, and you must cover all the subtrees, because it is impossible for the sum of the two subtrees to exceed the total number of leaf nodes, they must be connected.

#include <bits/stdc++.h> using namespace std; const int MAXN = 1e5+5; int n, h; int deg[MAXN]; vector<int> List, G[MAXN]; int ansu[MAXN], ansv[MAXN], acnt; int r[MAXN], cnt; bool vis[MAXN]; void dfs(int now) { vis[now] = 1; r[now] = cnt++; // printf("dfs now->%d r[now]->%d\n", now, r[now]); int sz = G[now].size(); for (int i=0; i<sz; ++i) { int v = G[now][i]; if (!vis[v]) dfs(v); } } bool cmp(int a, int b) { return r[a] < r[b]; } int main() { scanf("%d%d", &n, &h); int u, v; for (int i=0; i<n-1; ++i) { scanf("%d%d", &u, &v); ++deg[u]; ++deg[v]; G[u].push_back(v); G[v].push_back(u); } int ans = 0, root = 0; for (int i=0; i<n; ++i) { if (deg[i] == 1) List.push_back(i); else root = i; } dfs(root); sort(List.begin(), List.end(), cmp); int siz = (int)List.size(), del = (siz+1)/2; // printf("%d\n", siz); int ansc = siz/2; for (int i=0; i<ansc; ++i) { ansu[acnt] = List[i]; ansv[acnt++] = List[i+del]; } if (siz%2) { ++ansc; ansu[acnt] = List[siz/2]; ansv[acnt++] = List[0]; } printf("%d\n", ansc); for (int i=0; i<acnt; ++i) { printf("%d %d\n", ansu[i], ansv[i]); } return 0; }