1023. Funny Contest
Constraints
Time Limit: 3 secs, Memory Limit: 32 MB
Description
Powerful men often play funny game. Today N powerful men sit in a circle and face inward. They all have a power-value. If two men start a fight, the man with the bigger power-value will win. Otherwise there will be a draw if power-value is the same.
X-man invites (k-1) men in a consecutive sequence next to his left then makes a group. We call it X-man's k-group. Two k-group fight with each other.(1) Arrange the men in X-man's k-group by the distance from them to X-man in ascending order. (The distance from A-man to B-man is the number from A-man to B-man counterclockwise, including A-man and B-man. For example, the distance from a man to himself is 1, and the distance from a man to his left is 2 ...)(2) The fight between two groups--(x1,x2,...xk) and (y1,y2...yk)---will be a draw if xi=yi for all i(1<=i<=k). The first group will win if there is an i (1<=i<=k) where xi>yi, and xj=yj for all j (1<=j<i), and vice versa.We consider all the k-group and rank them. The rank of a group is the number of groups it can beat.
Now we consider the mark of X-man: the sum of the rank of all his k-groups. (k=1,2,4 ... 2^p, 2^p <=N and 2^(p+1)>N)
Input
The first line of the input is a single positive integer N (N<=100000). The next line contains N numbers, the power-value of the men in the circle, clockwise. Power-value is a positive integer which is smaller than 2^31.
Input will be ended by the end of file.
Output
For each test case, output one line containing the marks of the men corresponding to the position in the input. There is a space between two marks.
Sample Input
3 10 3 1
Sample Output
4 2 0
Hint
In the sample :
1-group
(10) (3) (1)
rank 2 1 0
2-group
(10,3) (3,1) (1 ,10)
rank 2 1 0
Problem Source
ZSUACM Team Member
这题如果暴力法的话会超时,网上查阅发现可以借用后缀数组来快速求出每个人每轮的排名,然后对每轮的排名利用计数排序进行处理算出每轮每个人战胜的人数,然后累加即可。后缀数组主要通过倍增算法实现,具体的算法详解和实现方法见链接吧:点击打开链接。
说实话我自己现在也没有彻底理解后缀数组的实现,只能依葫芦画瓢来写代码。
// Problem#: 1023
// Submission#: 5061703
// The source code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
// URI: http://creativecommons.org/licenses/by-nc-sa/3.0/
// All Copyright reserved by Informatic Lab of Sun Yat-sen University
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 210000
struct node{ //value保存值,label保存输入的顺序
int value;
int label;
};
int N;
int ans[maxn]; //答案数组,最终每个人的排名保存在其中
int counting_sort[maxn];//计数排序数组,用来统计每个排名出现的次数
int WA[maxn]; //用来保存排名,与Rank指针相关联,不直接用Rank[]数组是由于需要交换WA和WB数组的数据,用Rank指针来操作数组的话可以直接交换指针
int WB[maxn];//第二排序关键字的排名数组,与指针y相关联,即通过y指针来操作
int sa[maxn]; //后缀数组,s[i]保存的是排名第i的后缀的下标
int temp[maxn]; //临时数组
node powerman[maxn]; //保存输入的每个人的数据
int *Rank=WA;//Rank指针与WA数组绑定
int cp(int *r,int a,int b,int l) //用来判断两个后缀数组是否相同
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
void findAns(int currentrank)
{
int *t,*y=WB;
memset(ans,0,sizeof(ans));
memset(sa,0,sizeof(sa));
memset(WB,0,sizeof(WB));
for(int i=0;i<=currentrank;++i) counting_sort[i]=0; //清空计数排序数组
for(int i=0;i<=N*2;++i) counting_sort[Rank[i]]++; //计数排序
for(int i=1;i<=currentrank;++i) counting_sort[i]+=counting_sort[i-1];
for(int i=N*2;i>=0;i--) sa[--counting_sort[Rank[i]]]=i;//k=0时的sa数组
//下面是一个新的计数排序,用来求得k=1时每个人能超过的人数
for(int i=0;i<=N*2;++i) counting_sort[i]=0;
for(int i=1;i<=N;++i) counting_sort[Rank[i]]++;
for(int i=0;i<=N*2;++i) counting_sort[i]+=counting_sort[i-1];
for(int i=1;i<=N;++i) ans[i]+=counting_sort[Rank[i]-1];//将人数存入ans数组
for(int j=1,p=1;p<=N*2;j*=2,currentrank=p-1)
{
int i;
for(p=0,i=N*2-j+1;i<=N*2;i++) y[p++]=i;
for(i=0;i<=N*2;i++) if(sa[i]>=j)y[p++]=sa[i]-j;
for(i=0;i<=N*2;i++) temp[i]=Rank[y[i]];
for(i=0;i<=currentrank;i++) counting_sort[i]=0;
for(i=0;i<=N*2;i++) counting_sort[temp[i]]++;
for(i=1;i<=currentrank;i++) counting_sort[i]+=counting_sort[i-1];
for(i=N*2;i>=0;i--) sa[--counting_sort[temp[i]]]=y[i];
for(t=Rank,Rank=y,y=t,p=1,Rank[sa[0]]=0,i=1;i<=N*2;i++)
{
if(cp(y,sa[i-1],sa[i],j))
Rank[sa[i]]=p-1;
else
Rank[sa[i]]=p++;
} //求出k增大1之后的Rank数组
for(int l=0;l<=N*2;l++)//与上面类似,利用计数排序来统计每个人超过的人数
{
counting_sort[l]=0;
}
for(int l=1;l<=N;++l)
{
counting_sort[Rank[l]]++;
}
for(int l=1;l<=N*2;++l)
{
counting_sort[l]+=counting_sort[l-1];
}
for(int l=1;l<=N;++l)
{
ans[l]+=counting_sort[Rank[l]-1];
}
}
for(int l=1;l<=N;++l)//由于求Rank数组的操作进行的次数实际执行的次数比题目要求的多一次,所以将最后一次存入ans数组的数据减掉
{
ans[l]-=counting_sort[Rank[l]-1];
}
}
bool cmp(node A,node B)//sort排序的谓语函数
{
return A.value<B.value;
}
int main()
{
while(cin>>N)
{
memset(powerman,0,sizeof(powerman));
memset(WA,0,sizeof(WA));
Rank=WA;
for(int i=1;i<=N;++i)
{
cin>>powerman[i].value;
powerman[i].label=i;
}
sort(powerman+1,powerman+1+N,cmp);
int former=-1;
int currentrank=0;
for(int i=1;i<=N;++i)//powerman数组中的数据以按value升序排序,然后根据保存在节点中的输入顺序将其排名写入Rank数组
{
if(powerman[i].value!=former)
{
Rank[powerman[i].label]=++currentrank;
former=powerman[i].value;
}
else Rank[powerman[i].label]=currentrank;
}
for(int i=1;i<=N;++i)//由于所有人形成一个环,所以在Rank数组中将数据依次复制,使排名长度达到2*N
Rank[i+N]=Rank[i];
findAns(currentrank);
for(int i=1;i<=N-1;++i)
cout<<ans[i]<<" ";
cout<<ans[N]<<endl;
}
return 0;
}
502

被折叠的 条评论
为什么被折叠?



