2015 ACMICPC Asia Regional Changchun Online

27 篇文章 0 订阅

1001 Alisha’s Party即HDU 5437 Alisha’s Party

本题应该是很简单的模拟题。比赛时出现各种问题。最大的问题就是题意没看清,各种心浮气躁。

下面是AC代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=150005;
struct node
{
    int id,v;
    node(int id=0,int v=0):id(id),v(v){}
    friend bool operator <(node a,node b)
    {
        if(a.v!=b.v) return a.v<b.v;//如果权值不相同则权值大的放在堆顶
        return a.id>b.id;//否则先来的的放在堆顶
    }
    char s[205];
};
struct node1
{
    int x,y;
}a[maxn];
bool cmp(node1 a,node1 b)
{
    return a.x<b.x;
}
char ans[maxn][205];
int main()
{
    int t,n,m,k;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&k);
        queue<node>q;
        priority_queue<node>que;
        for(int i=1;i<=n;i++)
        {
            node tmp;
            scanf("%s%d",tmp.s,&tmp.v);//读入字符串和对应价值
            tmp.id=i;//字符串对应的ID
            q.push(tmp);
        }
        for(int i=0;i<m;i++) scanf("%d%d",&a[i].x,&a[i].y);
        sort(a,a+m,cmp);//这是坑点,数据给出的顺序时刻未必是有序的
        int p=0;
        for(int i=0;i<m;i++)
        {
            int num=a[i].y;
            while(q.front().id<=a[i].x)//把x时刻前的所有人用优先队列维护
            {
                que.push(q.front());
                q.pop();
            }
            while(num--&&que.size())//进入y个人
            {
                strcpy(ans[++p],que.top().s);
                que.pop();
            }
        }
        while(q.size())//把没进的都要加进去
        {
            que.push(q.front());
            q.pop();
        }
        while(que.size())
        {
            strcpy(ans[++p],que.top().s);
            que.pop();
        }
        for(int i=1;i<=k;i++)
        {
            int x;
            scanf("%d",&x);
            if(i>1) printf(" ");
            printf("%s",ans[x]);
        }
        puts("");
    }
    return 0;
}


1002 Ponds 即HDU 5438 Ponds

心好累,比赛时这道题思路对了,一下子加个特判,一下子加个变量,搞得好乱,比赛时没能AC。。。

这题不难的。

题目意思:给出一个无向图,删掉一些只连了一条边出去的或者没连边出去的顶点。删完之后再继续对删完后的图删点,这是一个递归的过程。

思路:删点的过程跟拓扑排序的过程极其相似,只不过这里是无向图。所以我们可以用类似拓扑排序的做法删点。然后对于处理后的图,只要BFS或DFS出所有连通块就行了。求所有联通块需要统计联通块的点的总数和所有点权之和。

下面是AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#define LL long long
using namespace std;
const int maxn=10005;
vector<int>G[maxn];
int n,m;
int w[maxn],cnt[maxn],del[maxn],vis[maxn];
void TopSort()
{
    queue<int>q;
    for(int i=1;i<=n;i++)
        if(cnt[i]==1) q.push(i);
    while(q.size())
    {
        int now=q.front();q.pop();
        del[now]=1;
        for(int i=0;i<G[now].size();i++)
        {
            int next=G[now][i];
            cnt[next]--;
            if(cnt[next]==1) q.push(next);
        }
    }
}
LL bfs(int u)
{
    queue<int>q;
    LL sum=0,ans=0;//分别统计联通块的点数和权值和
    q.push(u);
    vis[u]=1;
    while(q.size())
    {
        int now=q.front();q.pop();
        ans+=w[now];
        sum++;
        for(int i=0;i<G[now].size();i++)
        {
            int next=G[now][i];
            if(vis[next]==0&&del[next]==0)
            {
                vis[next]=1;
                q.push(next);
            }
        }
    }
    if(sum&1) return ans;//如果联通块的点数为奇数个
    return 0;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) G[i].clear();//清空图
        for(int i=1;i<=n;i++) scanf("%d",&w[i]);//读取点权
        memset(del,-1,sizeof(del));//记录对应的点是否被删除,-1代表是单点,0代表没被删,1代表被删了
        memset(vis,0,sizeof(vis));//
        memset(cnt,0,sizeof(cnt));//统计点出现了多少次
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x);
            cnt[x]++;cnt[y]++;
            del[x]=del[y]=0;
        }
        TopSort();//类似拓扑排序的删点
        LL ans=0;
        for(int i=1;i<=n;i++)
            if(vis[i]==0&&del[i]==0) ans+=bfs(i);//分别BFS出每个联通分块的权值
        printf("%I64d\n",ans);
    }
    return 0;
}

1005  Travel 即HDU 5441  Travel

题意:杰克喜欢周游世界,但他不喜欢等待。现在,他正在无向王国旅行。有n个城市和m连接城市双向道路。杰克讨厌等待时间过长上车,但他可以休息,在每一个城市。杰克只能站在停留在公共汽车上在有限的时间,超过限制时间就会发狂。假设你知道它需要去从一个城市到另一个城市,而且时间杰克能站停留一个总线上的时间为x分钟,多少对城市(A,B)可以让杰克可以从城市A到B旅行不抓狂呢?

思路:先对边排序,然后在对询问排序,并用并查集维护。

复杂度O(n+m)

下面是AC代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1000005;
int fa[maxn];//并查集
LL ans[maxn],num[maxn],res;//分别是存最后的答案,每个集合中的顶点数,维护当前解
struct edge { int from,to,w; }a[maxn];//存边
struct node { int id,w; }b[maxn];//存询问
bool cmp1(edge a,edge b) { return a.w<b.w;}//边排序规则
bool cmp2(node a,node b) { return a.w<b.w; }//询问排序规则
int Find(int x)//并查集查询部分
{
    if(fa[x]==x) return x;
    return fa[x]=Find(fa[x]);
}
void Merge(int x,int y)//并查集合并部分
{
    x=Find(x);
    y=Find(y);
    if(x!=y)//合并两个集合
    {
        fa[y]=x;//设原先集合个数分别为n和m,则对总数贡献了n*(n-1)+m*(m-1),如今
        //将它们合并了,集合个数变为n+m,故如今对总数贡献了(n+m)*(n+m-1),总的来说,集合合并了
        //比原先多贡献了(n+m)*(n+m-1)-{n*(n-1)+m*(m-1)}的总数
        res+=(num[x]+num[y])*(num[x]+num[y]-1)-num[x]*(num[x]-1)-num[y]*(num[y]-1);
        num[x]+=num[y];//计算完了才合并
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    int t,n,m,q;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&q);
        for(int i=0;i<=n;i++) fa[i]=i,num[i]=1;//并查集初始化
        for(int i=0;i<m;i++) scanf("%d%d%d",&a[i].from,&a[i].to,&a[i].w);
        for(int i=0;i<q;i++) scanf("%d",&b[i].w),b[i].id=i;
        sort(a,a+m,cmp1);sort(b,b+q,cmp2);
        int p=0;
        res=0;
        for(int i=0;i<q;i++)//有序地处理询问即可做到O(m)的复杂度,否则每次都要初始化并查集
        {
            while(a[p].w<=b[i].w&&p<m) Merge(a[p].from,a[p].to),p++;//如果没写p<m这个条件将会导致RE
            ans[b[i].id]=res;//res保存的就是当前限制下的总方案数,因为每合并一次,都计算出了这次的合并的增量
        }
        for(int i=0;i<q;i++) printf("%I64d\n",ans[i]);//最后依次按最初的询问次序输出即可
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值