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;
}
题意:杰克喜欢周游世界,但他不喜欢等待。现在,他正在无向王国旅行。有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;
}