P7166
代码较为简短清晰的一种实现
题意
给出三种场地中排第几的是哪个人(如样例一的最后一行表示第 \(3\) 个人排第一),并且给出两人之间决定在哪里打的方式。求最终的比赛情况。
思路
对于一般的情况,两个人在三个场地的排名各不相同,那么决定的因素就是两人的最小排名是多少。据此,我们可以按每个人的最小排名排序。
观察一个排序后的数组,我们根据每个人的最小排名,把数组分成数个连续段,同一段的最小排名相等。根据题意,这个段的大小至多为 \(3\)。
对于不同的段 \(i\),它总是可以确定这个段后面的每一个人与它比赛的情况,那么一般的情况就可以很方便的处理了。
而对于可能出现的相同最小值,根据题意,我们需要知道另一个人的具体排名。在 \(i\) 时暴力枚举后面的所有人显然是不现实的。转换一下,记录 \(i\) 前面不能确定的相同最小值的个数,把值相等的那些场地状压,同时分别记录对应的个数。每到一个新的人,就去解决前面遗留的问题。
对于段内,由于数量较少,可以直接暴力判断。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,cnt[N];
ll ans[5],pre[10];
struct per{
int ka,kb,kc,id;
}a[N];
struct node{
int x,y,id;
}tmp[5];
inline bool cmp(node xx,node yy){//模拟题目给出的排序规则
if(min(xx.x,xx.y)==min(yy.x,yy.y)) return max(xx.x,xx.y)==max(yy.x,yy.y)?xx.id<yy.id:max(xx.x,xx.y)<max(yy.x,yy.y);
return min(xx.x,xx.y)<min(yy.x,yy.y);
}
inline bool mcp(per x,per y){
return min(x.ka,min(x.kb,x.kc))<min(y.ka,min(y.kb,y.kc));
}
int main(){
scanf("%d",&n);
int t;
for(int i=1;i<=n;++i) scanf("%d",&t),a[t].ka=i;
for(int i=1;i<=n;++i) scanf("%d",&t),a[t].kb=i;
for(int i=1;i<=n;++i) scanf("%d",&t),a[t].kc=i,a[t].id=t;//含糊的题意
sort(a+1,a+1+n,mcp);
for(int l=1,r,num;l<=n;){
r=l,num=min(a[r].ka,min(a[r].kb,a[r].kc));
while(r<=n&&min(a[r].ka,min(a[r].kb,a[r].kc))==num) ++r;//找段
for(int i=l;i<r;++i){//处理前面遗留问题
for(int j=1;j<8;++j){//001~111
int mn=1e9,id=0;
if((j&1)&&a[i].ka<mn) mn=a[i].ka,id=0;
if((j&2)&&a[i].kb<mn) mn=a[i].kb,id=1;
if((j&4)&&a[i].kc<mn) mn=a[i].kc,id=2;
ans[id]+=pre[j];
}
}
for(int i=l;i<r;++i){
cnt[a[i].id]+=n-r+1;//打赢后面的
int st=0;
if(a[i].ka==num) st|=1;
if(a[i].kb==num) st|=2;
if(a[i].kc==num) st|=4;
++pre[st];
for(int j=i+1;j<r;++j){//暴力判断
tmp[0]={a[i].ka,a[j].ka,0};
tmp[1]={a[i].kb,a[j].kb,1};
tmp[2]={a[i].kc,a[j].kc,2};
sort(tmp,tmp+3,cmp);
if(tmp[0].x<tmp[0].y) ++cnt[a[i].id];
else ++cnt[a[j].id];
++ans[tmp[0].id];
}
}
l=r;
}
printf("%lld %lld %lld\n",ans[0],ans[1],ans[2]);
for(int i=1;i<=n;++i) printf("%d ",cnt[i]);
return 0;
}
记得开 longlong。

浙公网安备 33010602011771号