The 2026 ICPC Asia East Continent Online Contest (I)
A. Recall
给定丢失所有弹栈操作后的序列,在其中插入若干 -,使栈内元素始终互异且所有 T/F 询问正确,输出任意合法操作序列。
首先,如果没有 T 操作,进一个弹一个一定没有问题。
如果有 T 操作,那么形如 [+x...Tx],对于每一个 + 操作,记录每个 + 操作需要存活到第几轮,然后维护栈,栈顶元素不需要存活了就弹出。
cpp
#include <bits/stdc++.h>
using namespace std;
void solve()
{
int n;
cin >> n;
vector <char> op(n + 1);
vector <int> x(n + 1);
for(int i = 1; i <= n; i ++ )
cin >> op[i] >> x[i];
map <int, int> lstT;
vector <int> need(n + 1);
for(int i = n; i >= 1; i -- )
{
if(op[i] == 'T')
{
if(lstT[x[i]] == 0)
lstT[x[i]] = i;
}
if(op[i] == '+')
{
need[i] = max(i, lstT[x[i]]);
lstT[x[i]] = 0;
}
}
// for(int i = 1; i <= n; i ++ )
// cerr << need[i] << " ";
// cerr << "\n";
stack <int> s;
vector <char> ans;
for(int i = 1; i <= n; i ++ )
{
while(!s.empty() and need[s.top()] < i)
{
ans.push_back('-');
s.pop();
}
if(op[i] == '+')
{
ans.push_back('+');
s.push(i);
}
else
{
ans.push_back('?');
}
}
for(auto c : ans)
cout << c;
cout << "\n";
}
int main()
{
int T; cin >> T;
while(T -- ) solve();
}
C. Permutation Inversions
给定若干个区间内下标对应值的大小关系 ,构造一个满足所有关系的排列,使逆序对数量最少 ;无解输出 -1。
小的元素尽量前,大的元素尽量后。拓扑找关系即可,每次找一段里最大的 / 最小的尽量往后 / 往前放。
cpp
#include <bits/stdc++.h>
using namespace std;
void solve()
{
int n, m;
cin >> n >> m;
vector <int> covered(n + 1);
vector <vector<int>> G(n + 1);
vector <int> deg(n + 1);
for(int i = 1; i <= m; i ++ )
{
int l, r;
cin >> l >> r;
for(int j = l; j <= r; j ++ )
covered[j] = 1;
int lst;
for(int j = 1; j <= r-l+1; j ++ )
{
int x;
cin >> x;
if(j > 1)
{
G[x].push_back(lst);
deg[lst] ++ ;
}
lst = x;
}
}
vector <int> ans(n + 1);
priority_queue <int> q;
for(int i = 1; i <= n; i ++ )
{
if(!covered[i]) ans[i] = i;
else
{
int j = i;
while(j <= n and covered[j]) j ++ ;
for(int k = i; k < j; k ++ )
if(!deg[k])
q.push(k);
int mx = j - 1;
while(!q.empty())
{
int u = q.top(); q.pop();
if(ans[u]) {cout << "-1\n"; return ;}
ans[u] = mx; mx -- ;
for(auto v : G[u])
{
deg[v] -- ;
if(deg[v] == 0) q.push(v);
}
}
for(int k = i; k < j ; k ++ )
{
if(!ans[k]) {cout << "-1\n"; return ;}
}
i = j - 1;
}
}
for(int i = 1; i <= n; i ++ ) cout << ans[i] << " ";
cout << "\n";
}
signed main()
{
int T; cin >> T;
while(T -- ) solve();
}
D. Sequence
给你一个长度为 nnn 的 01 串 sss 中,每个位置 iii 前缀里与 sis_isi 不同的字符个数 pip_ipi 的乱序集合 。求有多少个 01 串能产生这个 ppp 集合。
主要是怎么处理 ppp 是乱序集合。
对于第 iii 位,如果与 i−1i-1i−1 位相同,则 pi=pi−1p_i = p_{i-1}pi=pi−1;如果与 i−1i-1i−1 位不同,则 pi=i−1−pip_i = i-1-p_ipi=i−1−pi。
记 pip_ipi 两种选择,记为 x,yx, yx,y。
不妨前 i−1i-1i−1 位有 xxx 个 000 和 yyy 个 111。
若 x=yx=yx=y,两种选择随便。
若 x<yx < yx<y 且 x∈Px \in Px∈P,则 sis_isi 必选 111。若选 000,则 pi=yp_i = ypi=y,下一手局面变为 x+1x+1x+1 个 000 和 yyy 个 111,x+1,yx+1,yx+1,y 均大于 xxx,PPP 中再也不会出现 xxx。
也就是说如果小的在 PPP 中,pip_ipi 必定是小的。
cpp
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
int main()
{
int n;
cin >> n;
vector <int> cntp(n + 1), p(n + 1);
for(int i = 1; i <= n; i ++ )
{
int x;
cin >> x;
cntp[x] ++ ;
}
long long ans = 1;
for(int i = 1; i <= n; i ++ )
{
int x = p[i-1], y = i-1-p[i-1];
if(x > y) swap(x, y);
if(x == y) ans = ans * 2 % mod;
if(cntp[x] > 0)
{
p[i] = x;
cntp[x] -- ;
}
else
{
p[i] = y;
cntp[y] -- ;
}
}
cout << ans;
}
L. Longest Common Prefix
每加入一个字符串,对前 iii 个字符串的每个 jjj,求任选 jjj 个串能得到的最大 LCP 长度 fi,jf_{i,j}fi,j,输出:
∑j=1i(fi,j⊕j). \sum_{j=1}^{i}(f_{i,j}\oplus j). j=1∑i(fi,j⊕j).
考虑从第 i−1i-1i−1 轮到第 iii 轮的变化。
Trie 上会新增一条路径,对于节点 cntcntcnt 从 c→c+1c \to c+1c→c+1,只可能对 fc+1f_{c+1}fc+1 产生影响,新的可能贡献为 depu⊕(c+1)dep_u \oplus (c+1)depu⊕(c+1)。
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
int son[N][26], cnt[N], dep[N], f[N];
int main()
{
int n;
cin >> n;
int tot = 0;
long long ans = 0;
for(int i = 1; i <= n; i ++ )
{
string s;
cin >> s;
int u = 0;
ans += i;
for(char c : s)
{
int &v = son[u][c-'a'];
if(!v)
{
v = ++ tot;
dep[v] = dep[u] + 1;
}
u = v;
cnt[u] ++ ;
if(u and dep[u] > f[cnt[u]])
{
ans -= f[cnt[u]] ^ cnt[u];
ans += dep[u] ^ cnt[u];
f[cnt[u]] = dep[u];
}
}
cout << ans << "\n";
}
}