2017-2018 ACM/ICPC, Asia Tsukuba Regional Contest

A.Secret of Chololate Poles

题意:白黑两种巧克力要相间放置在容器中,白的只能每次放1,黑的可以每次放1或者k。要求第一个和最后一个必须是黑的。问在总数不超过n的情况下有多少种放置方法。

题解:DP,dp[i][j]表示高度为i,最上面颜色为j的方案数。0代表黑色1代表白色。

状态转移方程dp[i+1][0]+=dp[i][1];dp[i+1][1]+=dp[i][0];dp[i+k][0]+=dp[i][1],要开long long。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 205
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,k;
ll dp[maxn][2];

int main()
{
    scanf("%d%d",&n,&k);
    memset(dp,0,sizeof(dp));
    dp[1][0]=1,dp[k][0]=1;
    for(int i=1;i<=n;i++)
    {
        dp[i+1][1]+=dp[i][0];
        dp[i+1][0]+=dp[i][1];
        dp[i+k][0]+=dp[i][1];
    }
    ll ans=0;
    for(int i=1;i<=n;i++)
        ans+=dp[i][0];
    printf("%lld\n",ans);
    return 0;
}

B. Parallel Lines

题意:平面上给出2n个点,将这些点两两相连,问最多能产生多少对平行线。

题解:枚举所有的两两组合的时间复杂度是O(15!!*16*16),可以接受。枚举时为将时间复杂度从O(16!)降至O(15!!),成对枚举的第一个数只选用最小的一种即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 17
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,ans;
int x[maxn],y[maxn];
bool vis[maxn];
int res[maxn];

double xie(int a,int b)
{
    if(x[a]==x[b])return -1.0*INF;
    return 1.0*(y[a]-y[b])/(x[a]-x[b]);
}

void judge()
{
    double tmp[maxn];
    int p=0;
    for(int i=0;i<n;i+=2)
        tmp[p++]=xie(res[i],res[i+1]);
    int cnt=0;
    for(int i=0;i<p;i++)
    {
        for(int j=i+1;j<p;j++)
        {
            if(tmp[i]==tmp[j])
                cnt++;
        }
    }
    ans=max(cnt,ans);
}

void dfs(int x,int st)
{
    if(x==n/2)
    {
        judge();
        return;
    }
    for(int i=st;i<=n;i++)
    {
        if(!vis[i])
        {
            vis[i]=1;
            res[x*2]=i;
            for(int j=i+1;j<=n;j++)
            {
                if(!vis[j])
                {
                    vis[j]=1;
                    res[x*2+1]=j;
                    dfs(x+1,i+1);
                    vis[j]=0;
                }
            }
            vis[i]=0;
            break;
        }
    }
}

int main()
{
    scanf("%d",&n);
    ans=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&x[i],&y[i]);
    }
    dfs(0,1);
    printf("%d\n",ans);
    return 0;
}

C. Medical Checkup

题意:有n个人依次进行检查,第i个人进行每项检查所需的时间都是h[i]。问在t时刻每个人正在进行或等待第几项检查。

题解:在进行第一项检查前,需要等待前面的所有人检查完毕。而进行后面的项目时只需等待前面一个人检查完即可。因此第一次检查的实际用时就是h[i]的前缀和sum[i],而后面各项检查的实际用时w[i]=max(w[i-1],h[i]),即如果前面一个人比后面的慢,就可以将这二者合并,此时后面人的等待时间必然是w[i-1]。最后的结果是(t-sum[i])/w[i]+2。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 100050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,t;
ll pre[maxn],h[maxn],rea[maxn];
ll Max(ll a,ll b){return a>b?a:b;}

int main()
{
    scanf("%d%d",&n,&t);
    pre[0]=rea[0]=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&h[i]);
        rea[i]=Max(h[i],rea[i-1]);
        pre[i]=pre[i-1]+h[i];
    }
    for(int i=1;i<=n;i++)
    {
        if(t<pre[i])printf("1\n");
        else printf("%lld\n",(t-pre[i])/rea[i]+2);
    }
    return 0;
}

F. Pizza Dilivery

题意:给定一个有向图,分别对每条边进行查询:若将这条边反向,图中从1到2的最短路会发生何种变化(变短/不变/变长)。

题解:首先考虑变短的情况:分别求原图中从1出发和反图中从2出发的单源最短路。对于原图中的每条边(u,v),如果1到v的最短距离加上2到u的最短距离再加上这条边长小于原图中1到2的最短路,即dis[v]+w+dit[u]<dis[2],这条边反向后就会使最短距离减小。

然后考虑变长的情况:如果一条边反向后最短路变长了,那么只有一种可能。从1到2的最短路或许不止一条,但它们必然都经过这条边,只有这样,这条边反向后所有的最短路才会都失效。

即:当且仅当边(u,v)是原图的有向图DAG的桥时,这条边反向后会使最短路增大。

因此先求出原图的有向图DAG,其中对于边(u,v)的判据是dis[u]+w+dit[v]==dis[2]。然后用Tarjan算法求出这个DAG的所有的桥,最后对每一条边判断是否为桥即可。

同时不满足上述两种情况的边,反向后对最短路长度没有影响。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<map>
#include<tuple>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 100050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,m,a,b,x;

struct graph
{
    int from[maxn],to[maxn],no;
    int head[maxn],nxt[maxn],val[maxn];
    ll dis[maxn];
    bool vis[maxn];
    
    void init()
    {
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        for(int i=0;i<=n;i++)dis[i]=INF;
        no=0;
    }
    
    void add(int a,int b,int x)
    {
        from[no]=a,to[no]=b;
        val[no]=x;
        nxt[no]=head[a];
        head[a]=no++;
    }
    
    void spfa(int st)
    {
        queue<int>q;
        vis[st]=1,dis[st]=0;
        q.push(st);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            vis[u]=0;
            for(int i=head[u];i!=-1;i=nxt[i])
            {
                int v=to[i];
                if(dis[v]>dis[u]+val[i])
                {
                    dis[v]=dis[u]+val[i];
                    if(!vis[v])
                    {
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
    }
}G,opG;

struct node
{
    int to[maxn],nxt[maxn],head[maxn],vis[maxn];
    int dfn[maxn],low[maxn],pre[maxn],val[maxn];
    int dep,no;
    map< tuple<int,int,int>,int >mo;
    
    void init()
    {
        memset(vis,0,sizeof(vis));
        memset(head,-1,sizeof(head));
        memset(dfn,-1,sizeof(dfn));
        memset(low,-1,sizeof(low));
        no=dep=0;
    }
    
    void add(int a,int b,int x)
    {
        val[no]=x;
        to[no]=b;
        nxt[no]=head[a];
        head[a]=no++;
    }
    
    void tarjan(int u,int fa)
    {
        dfn[u]=low[u]=++dep;
        vis[u]=1;
        for(int i=head[u];i!=-1;i=nxt[i])
        {
            int v=to[i];
            if(!vis[v])
            {
                tarjan(v,u);
                low[u]=min(low[u],low[v]);
                if(dfn[u]<low[v])
                {
                    if(u<v)mo[make_tuple(u,v,val[i])]++;
                    else mo[make_tuple(v,u,val[i])]++;
                }
            }
            else if(fa!=v)
                low[u]=min(low[u],dfn[v]);
        }
    }
}DAG;

int main()
{
    scanf("%d%d",&n,&m);
    G.init(),opG.init();
    for(int i=0;i<m;i++)
    {
        scanf("%d%d%d",&a,&b,&x);
        G.add(a,b,x);
        opG.add(b,a,x);
    }
    G.spfa(1);
    opG.spfa(2);
    DAG.init();
    for(int i=0;i<m;i++)
    {
        int u=G.from[i];
        int v=G.to[i];
        int w=G.val[i];
        if(G.dis[u]+w+opG.dis[v]==G.dis[2])
        {
            DAG.add(u,v,w);
        }
    }
    DAG.tarjan(1,0);
    for(int i=0;i<m;i++)
    {
        int u=G.from[i];
        int v=G.to[i];
        int w=G.val[i];
        if(G.dis[v]+w+opG.dis[u]<G.dis[2])
            printf("HAPPY\n");
        else
        {
            if(u>v)swap(u,v);
            if(DAG.mo[make_tuple(u,v,w)])
                printf("SAD\n");
            else printf("SOSO\n");
        }
    }
    return 0;
}

I. Starting a Scenic Railroad Service

题意:给出n个乘客的乘车区间,问在乘客自主选择座位和统一安排座位的情况下分别最少需要多少个座位。

题解:乘客自主选择座位的情况下,座位的最小数目是与某个乘车区间相交的区间数目的最大值。

相交的区间数目即:有多少区间是在这个区间内结束,或在这个区间内开始的。用前缀和分别处理上车和下车即可。

统一安排座位的情况下,座位的最小数目就是区间的最大覆盖数,同样用前缀和解决。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 200050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,x,y;
int st[maxn],en[maxn];
int sum[maxn];
int l[maxn],r[maxn];

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&x,&y);
        st[x]++;
        en[y]++;
        sum[x]++;
        sum[y]--;
        l[i]=x;
        r[i]=y;
    }
    for(int i=1;i<maxn;i++)
    {
        st[i]+=st[i-1];
        en[i]+=en[i-1];
        sum[i]+=sum[i-1];
    }
    int ans=0;
    for(int i=1;i<=n;i++)
        ans=max(ans,st[r[i]-1]-en[l[i]]);
    printf("%d ",ans);
    ans=0;
    for(int i=1;i<maxn;i++)
        ans=max(ans,sum[i]);
    printf("%d\n",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值