[COCI2015-2016#4] ENDOR 题解

[COCI2015-2016#4] ENDOR 题解

首先要发现一个很重要的性质,那就是两只变色龙碰撞后回头,等效于两只变色龙继续往前走,其中向右走的颜色不变,而向左走的要改变颜色。

那这样就有一种 \(O(n^2)\) 的做法:对于向右的变色龙,直接贡献答案;对于向左的变色龙,我们按照碰到的先后顺序枚举它前面所有向右走的变色龙,这样每只向右走的变色龙都会给当前的颜色贡献 \(\frac{1}{2}(d_{i+1} - d_i)\) 的路程,当然这里的 \(d\) 是相邻的两只向右走的变色龙。注意开头和结尾都要特殊处理。

然后我们发现 \(k\) 很小,而 \(d_{i+1} - d_i\) 仅与向右走的变色龙有关,这启示我们去按照模 \(k\) 意义上去记录贡献。我们约定后面所说的关于颜色的运算均为模 \(k\) 意义下的。具体地,我们设 \(w_i\),表示如果初始颜色为 \(0\),则当前的变色龙会给颜色 \(i\) 造成 \(w_i\) 的贡献。那对于颜色为 \(x\) 的变色龙,就会给颜色 \(x+i\) 造成 \(w_i\) 的贡献。

我们考虑维护这个 \(w_i\)。假设我们现在已经有 \(t\) 只向右走的变色龙,当加入一只新的向右走的变色龙 \(p\) 时,它的颜色会导致 \(w\) 数组集体平移,因为每个颜色都在模 \(k\) 意义下加了 \(c_p\)。所以 \(w'_{i+c_p} = w_i\),同时还会在 \(c_p\) 位置上新增 \(\frac{1}{2} (d_{t+1} - d_t)\) 的贡献。这样,每个向左走的变色龙就可以通过 \(k\) 次枚举统计答案了。还是注意开头和结尾的特殊处理,具体看代码。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+100;

int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch<'0' || ch>'9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch>='0'&&ch<='9') {x = x * 10 + (ch-48), ch = getchar();}
	return x * f;
}
struct xwx {
	int d, col, dir;
	bool operator < (const xwx &b) const {
		return d < b.d;
	}
} a[N];
int n, K, L;
int c[N];
double ans[50];
double w[50];
int sum;
int dr[N], totr, cr[N];//记录向右走的变色龙的信息

void change(int del) {
	double b[50];
	for(int i = 0; i<K; ++i) {
		b[(i+del)%K] = w[i];
	}
	for(int i = 0; i<K; ++i) {
		w[i] = b[i];
	}
}//平移数组
int main() {
	n = read(), K = read(), L = read();
	for(int i = 1; i<=n; ++i) {
		a[i].d = read(), a[i].col = read();
		char op[3];
		scanf("%s", op);
		if(op[0] == 'L') a[i].dir = 0;
		else a[i].dir = 1, ans[a[i].col]+=(L-a[i].d);
	}
	sort(a+1, a+n+1);
	for(int i = 1; i<=n; ++i) {
		c[i] = a[i].col;
	}//完全没有意义的一步(
	for(int i = 1; i<=n; ++i) {
		if(a[i].dir) {
			++totr;
			dr[totr] = a[i].d;
			cr[totr] = a[i].col;
			sum = (sum + a[i].col)%K;//用来确定颜色最后会变成什么
			if(totr > 1) {
				change(a[i].col);
				w[a[i].col]+=(0.5*(dr[totr] - dr[totr-1]));
			}	
			continue;
		}
//		dr[totr+1] = a[i].d;
//		for(int j = totr; j>=1; --j) {
//			ans[c[i]]+=(0.5*(dr[j+1] - dr[j]));
//			c[i] = (c[i] + cr[j])%K;
//		}
//		ans[c[i]]+=0.5*(a[i].d + dr[1]);//n^2做法

		for(int j = 0; j<K; ++j) {
			ans[(c[i]+j)%K]+=w[j];
		} 
		ans[c[i]]+=(0.5*(a[i].d - dr[totr]));//第一只遇到的向右走的变色龙
		ans[(c[i] + sum)%K]+=(0.5*(a[i].d + dr[1]));//走过最后一只变色龙后还要一直走到终点。
	}
	for(int i = 0; i<K; ++i) {
		printf("%.1lf\n", ans[i]);
	}
	return 0;
}
posted @ 2023-10-16 19:44  霜木_Atomic  阅读(14)  评论(0编辑  收藏  举报