
· 对于差分约束,题目要求求什么便设什么,令$Sum[i]$表示由$0 ~ i$的雇佣人数。
· 要充分利用题目所给条件,令$Have[i]$表示i时刻申报的人数,$Need[i]$表示i时刻需要的人数「结合 “人数” 关键词」。
此时容易列出两个基本不等式:
Sum[i] - Sum[i - 1] >= 0;
Sum[i] - Sum[i - 1] <= Have[i];
此时发现还有$Need[]$和范围为8个单位长度的区间没有使用,可得:
Sum[i]
- Sum[i - 8] >= Need[i]; (i >= 8)
那么还有环形无法处理。
对于环形的处理:
将整个$24$个单位的区间以当前点为分割点,将大区间分割为$(i - 8 + 24)$ ~ $i$ (环形)以及$i$ ~ $i + 16$,相当于覆盖了整个环形区间。
相当于是去限制非当前考虑的完整的区间从而达到限制当前因环形而断开的区间。
所以此时仅需考虑两个区间间的限制即可。
并且需要一个$Sum[- 1]$。
答案显然满足单调性,故考虑二分。
令$mid$表示需要$mid$个服务员。
则可列方程:
mid - (Sum[i + 16] - Sum[i]) >= Need[i]; (i < 8)
即可解答。
· 代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue> using namespace std; const int MAXN = + ;
const int MAXM = MAXN * ;
const int MAXT = + ; struct LinkedForwardStar {
int to;
int w; int next;
} ; LinkedForwardStar Link[MAXM << ];
int Head[MAXT]= {};
int size = ; void Insert (int u, int v, int w) {
Link[++ size].to = v;
Link[size].w = w;
Link[size].next = Head[u]; Head[u] = size;
} int T; int N; int Have[MAXT];
int Need[MAXN]; int Dist[MAXT];
int Count[MAXT]; queue<int> Que;
bool Inque[MAXT]; bool SPFA (int mid) {
memset (Dist, 0xcf, sizeof (Dist));
memset (Count, , sizeof (Count));
memset (Inque, , sizeof (Inque));
while (! Que.empty())
Que.pop(); Que.push();
Inque[] = true;
Dist[] = ; while (! Que.empty()) {
int u = Que.front();
Que.pop(); Inque[u] = false;
Count[u] ++; if (Count[u] >= )
return false; for (int i = Head[u]; i; i = Link[i].next) {
int v = Link[i].to, w = Link[i].w; if (Dist[u] + w > Dist[v]) {
Dist[v] = Dist[u] + w; if (! Inque[v]) {
Que.push(v);
Inque[v] = true;
}
}
}
} return Dist[] == mid;
} bool Check (int mid) {
memset (Head, , sizeof (Head));
size = ; Insert (, , mid);
Insert (, , );
Insert (, , - Have[]);
for (int i = ; i <= ; i ++) {
Insert (i - , i, );
Insert (i, i - , - Have[i]);
}
for (int i = ; i <= ; i ++)
Insert (i - , i, Need[i]);
for (int i = ; i < ; i ++)
Insert (i + , i, Need[i] - mid); return SPFA (mid);
} int main () {
scanf ("%d", & T); for (int Case = ; Case <= T; Case ++) {
memset (Have, , sizeof (Have)); for (int i = ; i <= ; i ++)
scanf ("%d", & Need[i]); scanf ("%d", & N); for (int i = ; i <= N; i ++) {
int start;
scanf ("%d", & start); Have[start] ++;
} bool flag = false;
int left = , right = N;
while (left < right) {
int mid = (left + right) >> ; if (Check (mid)) {
flag = true;
right = mid;
}
else
left = mid + ;
} if (! flag)
puts ("No Solution");
else
printf ("%d\n", left);
} return ;
} /*
1
1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
5
0
23
22
1
10
*/