Educational Codeforces Round 162 (Rated for Div. 2)(A~D)

A. Moving Chips

分析:先找出区间[l,r],a[l]为1第一次出现,a[r]为1最后一次出现,[l,r]以外的区间不用管。

所以我们要将[l,r]中所有1的区域块练成一块。

经过简单的样例分析发现,假设1的区域t1和区域t2间有x个0,按照最优的走法,只需要x步就能将这两块区域合成一块。

所以问题就转换成[l,r]有多少个0

int a[N];
void solve() {
	int n; cin >> n;
	int l = 1, r = n;
	int f = 0;
	rep(i, 1, n) {
		cin >> a[i];
		if (!f && a[i] == 1) {
			f = 1; l = i;
		}
		if (a[i] == 1) r = i;
	}
	int ans = 0;
	rep(i, 1, n) {
		if (i >= l && i <= r && a[i] == 0) ans++;
	}
	//tans;
	cout << ans << endl;
 
 
}

B. Monsters Attack!

分析:

1.当怪物1的坐标是-x,血量为t1,怪物2的坐标是x,血量为t2,那我们其实可以把他们当成同一个怪物,因为这两个怪物的威胁是相同的。所以我们就可以看成,有一个怪物在坐标x,血量为t1+t2

2.我们要优先解决离我们近的怪物,因此要对距离进行排序。我用的是map,用结构体排序也可以。

3.当前离我们最近的怪物t距离为c,假设我们在这c秒都把子弹打到怪物t身上,用一个rest记录怪物当前血量,rest>0就死了,rest<0说明伤害溢出了,没必要把所有伤害都打到这个怪物身上,应该把rest伤害留给下一个“离我们最近的怪物”。

int a[N],b[N];
void solve() {
	int n, k, cnt = 0; cin >> n >> k;
	map<int, int> mp;
	rep(i, 1, n) cin >> a[i];
	rep(i, 1, n) {
		cin >> b[i];
		if (b[i] < 0) b[i] = -b[i];
		mp[b[i]] += a[i];
	}
 
	//tans;
	int rest = 0;
	for (auto it : mp) {
		int c = it.first - cnt;
		mp[it.first] -= c * k - rest;
		rest = mp[it.first];
		if (rest > 0) {
			cout << "NO" << endl;
			return;
		}
 
		cnt += c;
	}
 
	cout << "YES" << endl;
 
 
}

C. Find B

分析:

对于1,我们必须要+1(题意说了必须正整数),对于大于1的数,我们可以-1。

[l,r]的长度为len,有x个数为1,len-x个数大于1。我们必须进行x次+1操作,当这len-x个数可以进行x次-1操作的话,就是YES

我们要以O(1)的时间复杂度查询到[l,r]中1的个数,所以要预处理(以类似前缀和的方法处理)。

int n, q;
int a[N], b[N], c[N];
void solve() {
	cin >> n >> q;
	rep(i, 1, n) {
		cin >> a[i];
		b[i] = b[i - 1] + a[i];
		c[i] = c[i - 1] + (a[i] == 1);
	}
 
	while (q--) {
		int l, r; cin >> l >> r;
		//tans;
		int len = r - l + 1;
		int cnt1 = c[r] - c[l - 1];
		int mmin = b[r] - b[l - 1] - cnt1 - (len - cnt1);
		if (mmin < cnt1 || len == 1) cout << "NO" << endl;
		else cout << "YES" << endl;
	}
 
}

D. Slimes

分析:前缀和,后缀和,二分我都想到了,公式也推出来了,但有这样的特殊情况{2,2,2,2},不知道怎么处理。

因为前缀和与后缀和的作用是我们可以查新,区间[l,r]的史莱姆经过互相吞噬后的大小,但如果区间[l,r]是{2,2,2,2},那么这个区间的史莱姆就不能相互吞噬(因为只有严格大于的史莱姆才能吞噬)。换言之,只要[l,r]区间的史莱姆不是全部相同的,那么[l,r]的史莱姆就可以经过相互吞噬,变成一个史莱姆。

看了题解,知道了要设pl和sl数组,来记录同一个数字连续出现了几次。

ps:为啥能想到二分呢,因为计算第i个史莱姆被吃掉的最小秒数,要去看i左边和i右边的史莱姆,这就可以想到二分了。

因为去看i左边时,相当于把i当成右端点,然后去二分查找左端点。

去看i右边时,相当于把i当成左端点,然后去二分查找右端点。

技巧:当题目要我们枚举端点,或者看到第k大,第k小之类的字眼,可以去往二分想一想。

int pre[N], suf[N],a[N],pl[N],sl[N];
void solve() {
	int n; cin >> n;
	rep(i, 1, n) {
		cin >> a[i];
		pre[i] = pre[i - 1] + a[i];
		pl[i] = 1;
		if (a[i] == a[i - 1]) pl[i] += pl[i - 1];
	}
	for (int i = n; i >= 1; i--) {
		suf[i] = suf[i + 1] + a[i];
		sl[i] = 1;
		if (a[i] == a[i + 1]) sl[i] += sl[i + 1];
	}
	//sort(suf + 1, suf + 1 + n);


	//tans;
	rep(i, 1, n) {
		if (i != 1) cout << " ";
		int k1 = inf, k2 = inf;
        if(a[i-1]>a[i]||a[i+1]>a[i]){
            cout<<1;
            continue;
        }

		//往左找
		int l = 1, r = i;
		while (l <= r) {
			int mid = (l + r) / 2;
			if (suf[mid] > a[i] + suf[i] && mid+sl[mid]<i) {
				l = mid + 1;
				k1 = mid;
			}
			else {
				r = mid - 1;
			}
		}
		if (k1 != inf) k1 = i - k1;

		//往右找
		l = i + 1, r = n;
		while (l <= r) {
			int mid = (l + r) / 2;
			if (mid - pl[mid] > i && pre[mid] - pre[i] > a[i]) {
				r = mid - 1, k2 = mid;
			}
			else l = mid + 1;
		}
		if (k2 != inf) k2 = k2 - i;


		int ans = min(k1, k2);
		if (ans == inf) ans = -1;
		cout << ans;

	}
	cout << endl;
    for (ll i = 1; i <= n; ++i)a[i] = pre[i] = suf[i] = pl[i] = sl[i] = 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值