【题解】[COCI2020-2021#1] Tenis

solution:

恶心题。考点:位运算 + 模拟。

我们将比赛分成两类:第一种是严格的胜利,即赢家的获胜位置严格小于输家的获胜位置。第二种是非严格的胜利,这意味着球员在球场上的获胜位置是相等的。

现在问题转化为在一个 3 列的网格上统计。提供 k c z n o l kcznol kcznol 大佬的 O ( n ) O(n) O(n) 算法,暴踩标算:

  1. 从左往右扫一遍,对于这一列的每个球员是第一次出现,那么我们统计 ( i + 1 , n ) (i+1,n) (i+1,n) 的答案;否则直接跳过。具体我们要维护 c n t [ 8 ] [ 3 ] cnt[8][3] cnt[8][3] 表示在状态 m a s k mask mask 的第 i i i 行排的最小且球场编号最小的球员的位置。
  2. 现在我们计算非严格胜利。枚举 i i i , i 2 i2 i2 ( i < i 2 ) (i<i2) (i<i2) , 如果 x x x, y y y 都不等于 0 0 0 ( x ≠ y ) (x\ne y) (x=y), 我们可以枚举 i 3 ∈ [ 0 , 2 ] i3\in [0,2] i3[0,2] , 使得在输家位置最靠前的情况下场地编号最小,就可以比较出 x x x , y y y 的结果。

常数大概有 2 3 ∗ 3 ∗ 3 = 72 2^3*3*3=72 2333=72

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
#define PII pair<int,int>
using namespace std;
const int mx=1e5+5;
int n,a[3][mx],id[mx][3],cnt[8][3],ans[mx];
ll ans2[3];
int in[mx];
//转化为一个三行的表格,然后求啥 ? 
inline int read() {
	int x=0; char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x;
}
void add(int j,int w) {
	for(int s=1;s<=7;s++) {
		int mn_i=-1;
		for(int i=0;i<=2;i++) {
			if(s>>i&1&&(mn_i==-1||id[j][i]<id[j][mn_i])) mn_i=i;
		}
		cnt[s][mn_i]+=w;
	}
}
signed main() {
    //  freopen("data.in","r",stdin);
    n=read();
    for(int i=0;i<=2;i++) {
    	for(int j=1;j<=n;j++) {
    		id[a[i][j]=read()][i]=j;
		}
	}
	for(int j=1;j<=n;j++) add(j,1);
	for(int j=1;j<=n;j++) {
		for(int i=0;i<=2;i++) {
			if(in[a[i][j]]) a[i][j]=0; //player a[i][j] 已经计算过了 
			else {
				add(a[i][j],-1);
				for(int i1=i;i1<=2;i1++) {
					//in[x] : 状压 
					if(a[i1][j]==a[i][j]) {
						in[a[i][j]]|=1<<i1;
					}
				}
			} 
		}
		for(int i=0;i<=2;i++) {
			if(a[i][j]) {
				int x=a[i][j];
				//cnt[i][j] 记录 状态为 i 的当中最优决策为 j 的 player 个数 
				for(int t=0;t<=2;t++) {
					ans2[t]+=cnt[in[x]][t];
					ans[x]+=cnt[in[x]][t];
				}
				for(int i1=i+1;i1<=2;i1++) {
					//对于非严格的处理(我们首先比较败者名次靠前,再比较场地编号) 
					if(a[i1][j]) { 
						int y=a[i1][j];
						PII mn={n+1,n+1};
						for(int t=0;t<=2;t++) {
							//此处 x 取得最优,当前得到的点对为 (id[y][t], t) 
							if(id[x][t]==j) {
								mn=min(mn,{id[y][t],t});
							}
							//此处 y 取得最优,当前得到的点对为 (id[x][t], t) 
							if(id[y][t]==j) {
								mn=min(mn,{id[x][t],t});
							}
						}
						//得到所举办的场地
						ans2[mn.second]++;
						ans[id[x][mn.second]==j?x:y]++; 
					}
					
				}
			}
		}
	}
	printf("%lld %lld %lld\n",ans2[0],ans2[1],ans2[2]);
	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值