給定長度為 n 的序列,所有區間 [i, j] 的和的符號(+/-/0)已知。求任意一組滿足條件的序列。
n ≤ 19,多測試用例。
S[0] = 0, S[k] = a[1] + ... + a[k] (1 ≤ k ≤ n)。
區間和 sum(i, j) = S[j] - S[i-1]。
sum(i, j) > 0 → S[j] > S[i-1] → 有向邊 i-1 → j(權重 1)sum(i, j) < 0 → S[j] < S[i-1] → 有向邊 j → i-1(權重 1)sum(i, j) = 0 → S[j] = S[i-1] → 雙向邊 i-1 ↔ j(權重 0),即強連通分量用 dis[u][v] = 0 表示 u=v。構圖時先將所有 0 關係合併:
遍歷節點,若 dis[u][v]==0 則賦予相同拓撲序值。
dis[n+1][n+1] 關係矩陣(-1/0/1)與入度 du[]suf[](前綴和相對值)suf[0] 歸零,輸出 a[i] = suf[i] - suf[i-1]// 構圖
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j++) {
char c; scanf(" %c", &c);
if (c == '+') { du[j]++; dis[i-1][j] = 1; dis[j][i-1] = -1; }
else if (c == '-') { du[i-1]++; dis[j][i-1] = 1; dis[i-1][j] = -1; }
// '0' 保持 dis=0
}
// 拓撲賦值
int tot = 0, val = 0;
while (tot < n + 1) {
for (int i = 0; i <= n; i++) if (du[i] == 0) {
// 收集所有與 i 相等的點
vector same = {i};
for (int j = 0; j <= n; j++)
if (i != j && du[j] != -1 && dis[i][j] == 0)
same.push_back(j);
// 賦相同值
for (int x : same) suf[x] = val;
val++; tot += same.size();
// 刪除邊
for (int x : same)
for (int r = 0; r <= n; r++)
if (du[r] != -1 && dis[x][r] == 1) du[r]--;
for (int x : same) du[x] = -1;
}
}
// 輸出原序列
int base = suf[0];
for (int i = 1; i <= n; i++) {
if (i > 1) putchar(' ');
printf("%d", suf[i] - base - (suf[i-1] - base));
}
| 細節 | 處理 |
|---|---|
| 多測試用例 | 每 case 重置 dis, du |
| 輸入符號間可能有空格 | scanf(" %c", &c) 跳過空白 |
| 相等節點必須同步入隊 | 收集 dis[i][j]==0 的所有點一次性處理 |
| 輸出格式 | 數字間單空格,行末無空格 |
本題展示了 「區間和符號 → 前綴和差分 → 偏序圖 → 拓撲排序賦值」 的經典建模鏈路。核心洞見:等號意味著變量合併(強連通分量),不等號意味著偏序關係。這類「符號約束求任意解」問題,拓撲排序是通用利器。