Gym -102007 :Benelux Algorithm Programming Contest (BAPC 18) (寒假自训第5场)

A .A Prize No One Can Win

题意:给定N,S,你要从N个数中选最多是数,使得任意两个之和不大于S。

思路:排序,然后贪心的选即可。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
ll a[maxn];
int main()
{
    int N,ans; ll M;
    scanf("%d%lld",&N,&M);
    rep(i,1,N) scanf("%lld",&a[i]);
    sort(a+1,a+N+1);
    ans=1;
    rep(i,2,N){
        if(a[i]+a[i-1]<=M) ans=i;
    }
    printf("%d\n",ans);
    return 0;
}
View Code

 

B .Birthday Boy

题意:给出N个人的生日,让你选择一天,使得这一天的前一个生日距离它最远,如果有多个一样的,有点选择10月27之后的

思路:模拟即可。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
int Y[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int vis[1000],tot;
int mp[13][32],C[13][32],A[1000],B[1000];
int main()
{
    int N,y,d,ansx=1,ansy=1,ans=-1;char c[20];
    scanf("%d",&N);
    rep(i,1,N){
        scanf("%s%d-%d",c+1,&y,&d);
        mp[y][d]=1;
    }
    rep(i,1,12)
     rep(j,1,Y[i]){
        tot++; A[tot]=i; B[tot]=j; C[i][j]=tot;
        if(mp[i][j]) vis[tot]=1;
    }
    rep(i,1,tot) vis[i+tot]=vis[i],A[i+tot]=A[i],B[i+tot]=B[i];
    rep(i,tot,tot+tot-1) {
        if(vis[i]) continue;
        int j=i; while(j-1>=i-tot+1&&!vis[j-1]) j--;
        if(i-j>ans){
            ans=i-j,ansx=A[i],ansy=B[i];
        }
        else if(i-j==ans&&C[ansx][ansy]<=C[10][27]&&C[A[i]][B[i]]>C[10][27]) ansx=A[i],ansy=B[i];
    }
    printf("%02d-%02d\n",ansx,ansy);
    return 0;
}
View Code

 

C .Cardboard Container

题意: 给定N个1*1*1的盒子,让你用最小的表面积把它包起来.

思路: 枚举长和宽和高(因子)即可.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=100+10;
const int inf=1000000000;
int main()
{
    int v;
    cin>>v;
    ll ans=inf;
    for(int i=1;i<=v;i++){
        if(v%i==0){
            int l=v/i;
            for(int j=1;j<=l;j++){
                if(l%j==0){
                    int k=l/j;
                    ans=min(ans,2LL*(i*j+k*j+k*i));
                }
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}
View Code

 

E .Entirely Unsorted Sequences

https://blog.csdn.net/ccsu_cat/article/details/86754931

 

G .Game Night

模拟几种排列即可

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
int sum[maxn][3],N; char c[maxn];
int get(int A,int B,int C)
{
    int res=0;
    rep(i,1,N){
        int X=sum[i+sum[N][A]-1][A]-sum[i-1][A];
        int Y=sum[i+sum[N][A]+sum[N][B]-1][B]-sum[i+sum[N][A]-1][B];
        int Z=sum[i+N-1][C]-sum[i+sum[N][A]+sum[N][B]-1][C];
        res=max(res,X+Y+Z);
        //cout<<A<<" "<<B<<" "<<C<<" : "<<i<<" "<<res<<endl;
    }
    return N-res;
}
int main()
{
    scanf("%d%s",&N,c+1);
    int ans=N;
    rep(i,1,N) c[N+i]=c[i];
    rep(i,1,N+N){
        rep(j,0,2) sum[i][j]=sum[i-1][j];
        if(c[i]=='A') sum[i][0]++;
        if(c[i]=='B') sum[i][1]++;
        if(c[i]=='C') sum[i][2]++;
    }
    rep(i,0,2)
     rep(j,0,2)
      rep(k,0,2)
       if(i!=j&&i!=k&&j!=k)
        ans=min(ans,get(i,j,k));
    printf("%d\n",ans);
    return 0;
}
View Code

 

I. In Case of an Invasion, Please...

题意:给定N城市每个城市人数是Pi,M双向路,其中S个点(S<=10)是避难所,避难所容量是Ci,(N<1e5; Ci,Pi<1e9)最小化最大的转移时间,使得所有人都转移到避难所。

思路:二分+最大流。由于图比较大,此题需要一些优化才能过。

由于上限是1e14; 所以二分次数是50次; 我们可以把距离离散化上线变为了1e6,二分次数是20;这是一个优化。

第二个优化,对于二分到的mid,如果一个城市可以去的避难所容量之和<P[i],那么显然不用跑网络流也知道不合法。

还有就是sap跑二分图比较慢,要用dinic。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+150;
ll inf=1ll<<60;
struct Edge
{
    int from,to;
    ll cap,flow;
};
struct Dinic
{
    int n,m,s,t;
    vector<Edge>edges;
    vector<int>G[maxn];
    bool vis[maxn];
    int d[maxn],cur[maxn];
    void init(int n)
    {
        this->n=n;
        for(int i=0;i<n;i++)
        G[i].clear();
        edges.clear();
    }
    void AddEdge(int from,int to,ll cap)
    {
        edges.push_back((Edge){from,to,cap,0});
        edges.push_back((Edge){to,from,0,0});
        m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    bool bfs()
    {
        memset(vis,0,sizeof(vis));
        queue<int>Q;
        Q.push(s);
        d[s]=0;
        vis[s]=1;
        while(!Q.empty())
        {
            int x=Q.front();Q.pop();
            for(int i=0;i<G[x].size();i++)
            {
                Edge& e=edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow)
                {
                    vis[e.to]=1;
                    d[e.to]=d[x]+1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    ll dfs(int x,ll a)
    {
        if(x==t||a==0)return a;
        ll flow=0,f;
        for(int& i=cur[x];i<G[x].size();i++)
        {
            Edge& e=edges[G[x][i]];
            if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0)
            {
                e.flow+=f;
                edges[G[x][i]^1].flow-=f;
                flow+=f;
                a-=f;
                if(a==0)break;
            }
        }
        return flow;
    }
    ll Maxflow(int s,int t)
    {
        this->s=s,this->t=t;
        ll flow=0;
        while(bfs())
        {
            memset(cur,0,sizeof(cur));
            flow+=dfs(s,inf);
        }
        return flow;
    }
}solve;
ll d[15][maxn],sum,vis[maxn];
vector<int>G[maxn],dis[maxn];
struct node
{
    int u;
    ll w;
    node(int a,ll b)
    {
        u=a,w=b;
    }
    bool operator<(const node&t)const
    {
        return w>t.w;
    }
};
priority_queue<node>q;
void dij(int k,int s,int n)
{
    for(int i=1;i<=n;i++)d[k][i]=inf,vis[i]=0;
    d[k][s]=0;
    q.push(node(s,0));
    while(!q.empty())
    {
        node e=q.top();q.pop();
        int u=e.u; vis[u]=0;
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(d[k][v]>d[k][u]+dis[u][i])
            {
                d[k][v]=d[k][u]+dis[u][i];
                if(!vis[v]) vis[v]=1,q.push(node(v,d[k][v]));
            }
        }
    }
}
int p[maxn],s[maxn],c[maxn],tot; ll B[maxn*20],fcy[maxn];
int main()
{
    int n,m,k,u,v,w;
    scanf("%d%d%d",&n,&m,&k);
    int S=0,T=n+k+1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&p[i]);
        sum+=p[i];
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        G[u].push_back(v);
        dis[u].push_back(w);
        G[v].push_back(u);
        dis[v].push_back(w);
    }
    for(int i=1;i<=k;i++)
    {
        scanf("%d%d",&s[i],&c[i]);
        dij(i,s[i],n);
    }
    for(int i=1;i<=k;i++)
     for(int j=1;j<=n;j++) B[++tot]=d[i][j];
    sort(B+1,B+tot+1);
    tot=unique(B+1,B+tot+1)-(B+1);
    ll l=1,r=tot,mid,ans;
    while(l<=r)
    {
        mid=(l+r)/2;
        solve.init(T+1);
        for(int i=1;i<=n;i++) fcy[i]=0;
        for(int i=1;i<=n;i++)
        for(int j=1;j<=k;j++)
        if(d[j][i]<=B[mid])
        solve.AddEdge(i,n+j,inf),fcy[i]+=c[j];
        bool F=true;
        for(int i=1;i<=n;i++)
        if(fcy[i]<p[i]){ F=false; break;}
        if(!F) {l=mid+1;continue;}
        for(int i=1;i<=n;i++)
        solve.AddEdge(S,i,p[i]);
        for(int i=1;i<=k;i++)
        solve.AddEdge(n+i,T,c[i]);
        ll res=solve.Maxflow(S,T);
        if(res==sum) r=mid-1,ans=B[mid];
        else l=mid+1;
    }
    printf("%lld\n",ans);
}
View Code

 

J .Janitor Troubles

题意:给定ABCD四条边,让你组成一个面积最大的四边形,保证有解。

思路:比赛时三分做的,1A。但是赛后拿去做hduwa掉了,还是没弄清楚。

三分:枚举相邻边的组合形式,然后对对角线进行三分。 结论是当三分到对角线的对面两个角都是直角时,答案最大,此时正好有外接圆。

定理:根据上面结论,相当于四边形是圆内接四边形,我们可以至直接用海伦公式的变形;

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
double X[5],ans;
double S(double x,double y,double x1,double y1,double z)
{
    double p=(x+y+z)/2,p1=(x1+y1+z)/2;
    return sqrt(p*(p-x)*(p-y)*(p-z))+sqrt(p1*(p1-x1)*(p1-y1)*(p1-z));;
}
double get(int i,int j,int k,int p)
{
    double Mn=max(X[j]-X[i],X[p]-X[k]),Mx=min(X[j]+X[i],X[p]+X[k]);
    if(Mn>Mx) return 0.0;
    int T=100; double L=Mn,R=Mx,res=0.0;
    while(T--){
        double Mid1=L+(R-L)/3,Mid2=R-(R-L)/3;
        double F1=S(X[i],X[j],X[k],X[p],Mid1);
        double F2=S(X[i],X[j],X[k],X[p],Mid2);
        if(F1>=F2) res=max(F1,res),R=Mid2;
        else res=max(F2,res),L=Mid1;
    }
    return res;
}
int main()
{
    rep(i,1,4) scanf("%lf",&X[i]);
    sort(X+1,X+4+1);
    rep(i,1,4)
     rep(j,i+1,4)
      rep(k,1,4)
       rep(p,k+1,4)
        if(i!=k&&i!=p&&j!=k&&j!=p){
         ans=max(ans,get(i,j,k,p));
    }
    printf("%.10lf\n",ans);
    return 0;
}
View Code
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
double X[5],ans;
double get()
{
    double res=0.0,p=(X[1]+X[2]+X[3]+X[4])/2;
    res=sqrt((p-X[1])*(p-X[2])*(p-X[3])*(p-X[4]));
    return res;
}
int main()
{
    rep(i,1,4) scanf("%lf",&X[i]);
    ans=get();
    printf("%.10lf\n",ans);
    return 0;
}
View Code

 

K .Kingpin Escape

题意:给定一棵有根树,现在让你加最少的边,使得无论原图上哪条边被砍掉,从任意点出发都可以回到根节点。

思路:首先把保证至少两个点与根相邻,所以如果根只有一条边与它相邻,它需要加边。 其次,所有叶子节点需要加边,因为他和父亲被砍断后就GG了。

 我们按照一定顺序把这些点连边即可。 假设X个点需要连边,那么最少需要加(X+1)/2条边。然后两两连边即可,但是注意至少一条新加的边连通了根的两个子树。

所以我们按照DFS序,然后q[i]+q[X/2+i]连边,这样可以保证,并不是所有新加的边都在同一个子树(这样可能合法,因为砍掉根与儿子后,子树无法到达根)里。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
int Laxt[maxn],Next[maxn],To[maxn];
int sz[maxn],cnt,q[maxn],tot;
void add(int u,int v){
    Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v;
}
void dfs(int u,int f)
{
    if(sz[u]==1) q[++tot]=u-1;
    for(int i=Laxt[u];i;i=Next[i])
     if(To[i]!=f) dfs(To[i],u);
}
int main()
{
    int N,M,u,v;
    scanf("%d%d",&N,&M); M++;
    rep(i,1,N-1){
        scanf("%d%d",&u,&v);
        u++; v++; sz[u]++; sz[v]++;
        add(u,v); add(v,u);
    }
    dfs(M,0);
    int ans=(tot+1)/2;
    printf("%d\n",ans);
    rep(i,1,tot/2) printf("%d %d\n",q[i],q[i+tot/2]);
    if(tot&1) printf("%d %d\n",q[1],q[tot]);
    return 0;
}
View Code

 

posted @ 2019-02-06 16:04  nimphy  阅读(550)  评论(0编辑  收藏