Codeforces Round #600 (Div. 2)

B. Silly Mistake

題意
有一個公司,每天有 n 個員工進出,標號1~n。

公司對進入辦公室有一些規定:

  • 每個員工每天最多隻能進一次辦公室。
  • 如果那天他沒有進辦公室的話,他就沒有辦法離開。
  • 每天開始和結束時,辦公室都是空的(員工不能過夜)。

滿足以上三個條件的事件序列被稱爲有效日。

現給你一個序列 a,序列 a表示了員工的出入情況,“k"代表員工k進入辦公室,”-k"代表員工 k 出了辦公室。 例如:
[2, -2, 3, -3]
就代表 2 號員工進入了辦公室, 2 號員工出了辦公室, 3 號員工進入了辦公室, 3 號員工出了辦公室。

問這個序列 a 是否可以分成若干天的序列,保證每天都是有效的序列,如果無法分配直接輸出 -1 ,否則輸出可分的天數 和 每天的元素個數。

思路
模擬題
首先考慮非法的情況:

  • 員工在一天進入多次
  • 進入的員工沒有出來
  • 沒有進員工,就要出員工

下面就來解決非法的情況:
記錄序列 a 的前綴和,找出裏面前綴和爲 0 的情況
前綴和爲 0 就說明當前元素到 上一個前綴和爲 0 的元素之間的所有所有元素 在不重複的情況下 可以放在一天。

記錄每個正數出現的的次數,把出現的次數和前綴和中0 的個數比較,當其次數大於前綴和中的0的時候,意味着 有一個員工多次的進入,有元素重複。

當前綴和小於 0 的時候,意味着有沒有進員工,就要出員工的情況存在。

/***********************
*author:ccf
*source:Codeforces_600-B. Silly Mistake
*topic:
************************/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#define ll __int64
using namespace std;

const int N = 1e6 + 7;
int cas,n,cnt = 0;
ll a[N],s[N],bk[N] = {0};
vector<ll> ans;

bool check() {
	int len = ans.size() - 1;
	for(int i = 1; i <= n; i++) {
		if(s[i] == 0) len--;
		if(s[i] < 0)  return true;
		if(a[i] < 0 )  bk[-a[i]]--;
		if(a[i] > 0 && bk[a[i]] > len) return true;
	}
	return false;
}
int main() {
	//freopen("data.in","r",stdin);
	scanf("%d",&n);
	ans.push_back(0);
	ll tmp = 0;
	for(int i = 1; i <= n; i++) {
		scanf("%I64d",&a[i]);
		tmp += a[i];
		if(a[i] > 0)
			bk[a[i]]++;	
		else if(a[i] < 0) {
			if(bk[-a[i]] == 0) {
				printf("-1");
				return 0;
			}
		}
		s[i] = a[i] + s[i-1];
	}
	for(int i = 1; i <= n; i++) {
		if(s[i] == 0) {
			cnt++;
			ans.push_back(i);
		}
	}
	if(check() || tmp != 0 || n % 2 ==1) {
		printf("-1\n");
	} else {
		printf("%d\n",cnt);
		int len = ans.size();
		for(int i = 1; i < len; i++) {
			printf("%I64d ",ans[i]- ans[i-1]);
		}
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章