【游记】NOI Online 2022
感觉题目不是很难,前两题比较轻松,第三题也不难,大概 10:30 的时候写完检查完就交了。
民间数据发现 T2 输出 printf("%d %d\n", v[y], x)
写成了 printf("%d %d\n", v[y], y)
挂了/ll。
感觉挺活该的,写完后只对比了一下 YES 和 NO 是否正确,甚至没有 check 自己输出是否合法就交了。
不过也是幸运的至少不是在省选/NOI上挂分,至少下次遇到 OI 赛制的 spj 题肯定会手写 checker 验证一下。
T1
一眼题,先从 \(1\to n\) 扫一遍,\(p_i\) 表示扫到 \(i\) 时的栈顶,那么询问 \((l,r)\) 即为区间 \([l,r]\) 中 \(p_i < l\) 的 \(i\) 个数,离线树状数组即可,复杂度 \(\mathcal{O}(N\log N)\)。
#define N 500005
int n, m, a[N], b[N], sta[N], top, p[N], ans[N], c[N];
vector<Pr>u[N];
inline void add(int x){for(; x <= n; x += x & -x)c[x]++;}
inline int ask(int x){int sum = 0; for(; x; x -= x & -x)sum += c[x]; return sum;}
int main() {
//freopen("stack.in","r",stdin);
//freopen("stack.out","w",stdout);
read(n, m);
rp(i, n)read(a[i]);
rp(i, n)read(b[i]);
rp(i, n){
while(top && (a[sta[top]] == a[i] || b[sta[top]] <= b[i]))top--;
if(top)p[i] = sta[top];
sta[++top] = i;
}
rp(i, m){
int x, y; read(x, y);
ans[i] = - x + 1;
u[y].pb(mp(x - 1, i));
}
int s = 0;
rp(i, n){
if(!p[i])s++;
else add(p[i]);
go(x, u[i])ans[x.se] += ask(x.fi) + s;
}
rp(i, m)printf("%d\n", ans[i]);
return 0;
}
T2
将 \(size\) 相同的集合放在一起,哈希判重,将完全相同的集合只保留一个。
先考虑 \(size\) 相同的匹配,因为不存在完全相同的集合,所以只要一个位置上有两个集合包括它,那么就是合法匹配。
然后 \(size\) 从大往下扫,维护 \(c_i\) 表示位置 \(i\) 被哪个集合占了。如果扫到当前集合,所有位置的 \(c_i\) 相同,说明被包含直接忽略,否则 \(c_i\) 和当前集合一定是合法匹配。
不知道输入的集合是否有序,如果无序还要排序多一个 log。
#define N 1000005
typedef unsigned long long ull;
int n, v[N], u[N];
unordered_map<ull, int>h;
vector<int>a[N], c[N];
bool calc(int id){
go(x, c[id]){
go(y, a[x])if(v[y]){
printf("YES\n%d %d\n", v[y], x);
return true;
}
go(y, a[x])v[y] = x;
}
go(x, c[id]){
int lst = u[a[x][0]]; bool flag = false;
go(y, a[x]){
v[y] = 0;
if(u[y] != lst)flag = true;
}
if(flag){
puts("YES");
int p = 0;
go(y, a[x])if(u[y]){
if(!p || si(a[p]) > si(a[u[y]]))p = u[y];
}printf("%d %d\n", p, x); return true;
}
}
go(x, c[id])go(y, a[x])u[y] = x;
return false;
}
void solve(){
read(n), h.clear();
rp(i, n)c[i].clear(), u[i] = v[i] = 0;
rp(i, n){
int k; read(k);
if(!k)continue;
a[i].clear();
int lst = 0, flag = 0;
rp(j, k){
int x; read(x);
flag |= x < lst, lst = x;
a[i].pb(x);
}
if(flag)sort(a[i].begin(), a[i].end());
ull s = 0;
rp(j, k)s = s * B + (a[i][j - 1]);
if(h.count(s))continue;
h[s] = 1, c[k].pb(i);
}
pr(i, n)if(si(c[i]))if(calc(i))return;
puts("NO");
}
int main() {
// freopen("discuss.in","r",stdin);
// freopen("discuss.out","w",stdout);
int T; read(T);
while(T--)solve();
return 0;
}
T3
\(m = 2\) 时,\(Ans = \sum a_{i,j} \times (n + n)\)。
\(m = 3\) 时,考虑枚举 \(x\),表示 \(a_{x,i} + a_{x,j}\) 作为答案的情况。以 \((a_{y,i} - a_{x,i}, a_{z,i} - a_{x,i})\) 作为二元组,离线二维数点即可。
对于 \(m=4\) 的情况,我们需要求 \(\min + \max\) 而不是单独求 \(\min\) 或 \(\max\) 显然是有性质。
这提示已经非常明显了,我们将 \(\min\) 容斥掉,\(\min\{S_4\} = \max\{S_3\} - \max\{S_2\} + \max\{S_1\} - \max\{S_4\}\),不难发现 \(-\max\{S_4\}\) 和我们要求的 \(\max\) 抵消了,问题转化为 \(m = 3\) 的情况。
时间复杂度 \(\mathcal{O}(2^MN\log N)\)。
#define N 200005
int m, n, u[4][N], M = N - 4; LL ans;
struct node{
int x, y, w;
bool operator<(const node o)const {
if(x != o.x)return x < o.x;
return y < o.y;
}
}a[N], b[N];
LL c[2][N << 1];
inline void add(int op, int x,int val){for(; x <= M + M; x += x & -x)c[op][x] += val;}
inline LL ask(int op,int x){LL sum = 0; for(; x; x -= x & -x)sum += c[op][x]; return sum;}
void calc(int l,int r){
rp(i, n)
b[i] = node{M - a[i].x, M - a[i].y, a[i].w}, a[i].x += M - l, a[i].y += M - r;
sort(a + 1, a + n + 1), sort(b + 1, b + n + 1);
int j = 1;
memset(c, 0, sizeof(c));
rp(i, n){
while(j <= n && b[j].x <= a[i].x)add(0, b[j].y, 1), add(1, b[j].y, b[j].w), j++;
ans += ask(1, a[i].y) + ask(0, a[i].y) * a[i].w;
}
}
Pr p[N], q[N];
LL solve2(int l,int r){
rp(i, n)p[i] = mp(u[l][i] - u[r][i], u[l][i]), q[i] = mp(u[r][i] - u[l][i], u[l][i]);
sort(p + 1, p + n + 1), sort(q + 1, q + n + 1);
LL cnt = 0, sum = 0, ed = 0; int j = 1;
rp(i, n){
while(j <= n && q[j].fi <= p[i].fi)sum += q[j].se, cnt ++, j++;
ed += cnt * p[i].se + sum;
}
rp(i, n)p[i] = mp(u[r][i] - u[l][i], u[r][i]), q[i] = mp(u[l][i] - u[r][i], u[r][i]);
sort(p + 1, p + n + 1), sort(q + 1, q + n + 1);
cnt = 0, sum = 0, j = 1;
rp(i, n){
while(j <= n && q[j].fi < p[i].fi)sum += q[j].se, cnt ++, j++;
ed += cnt * p[i].se + sum;
}
//cout << "ac " << ed << endl;
return ed;
}
LL solve1(int x){
LL w = 0;
rp(i, n)w += u[x][i] * 1LL * (n + n);
//cout << "kk " << w << endl;
return w;
}
void solve3(int x,int y,int z){
rp(i, n)a[i] = node{u[x][i] - u[y][i], u[x][i] - u[z][i], u[x][i]};
calc(1, 1);
rp(i, n)a[i] = node{u[y][i] - u[x][i], u[y][i] - u[z][i], u[y][i]};
calc(0, 1);
rp(i, n)a[i] = node{u[z][i] - u[x][i], u[z][i] - u[y][i], u[z][i]};
calc(0, 0);
}
int main() {
//freopen("sort.in","r",stdin);
//freopen("sort.out","w",stdout);
read(m, n);
rep(i, 0, m - 1)rp(j, n)read(u[i][j]);
if(m == 2){
rp(i, n)ans += (u[0][i] + u[1][i]) * 1LL * (n + n);
printf("%lld\n", ans);
}
else if(m == 3){
// max
rp(i, n)a[i] = node{u[0][i] - u[1][i], u[0][i] - u[2][i], u[0][i]};
calc(1, 1);
rp(i, n)a[i] = node{u[1][i] - u[0][i], u[1][i] - u[2][i], u[1][i]};
calc(0, 1);
rp(i, n)a[i] = node{u[2][i] - u[0][i], u[2][i] - u[1][i], u[2][i]};
calc(0, 0);
//cout << "ss " << ans << endl;
//min
rp(i, n)a[i] = node{u[1][i] - u[0][i], u[2][i] - u[0][i], u[0][i]};
calc(1, 1);
rp(i, n)a[i] = node{u[0][i] - u[1][i], u[2][i] - u[1][i], u[1][i]};
calc(0, 1);
rp(i, n)a[i] = node{u[0][i] - u[2][i], u[1][i] - u[2][i], u[2][i]};
calc(0, 0);
printf("%lld\n", ans);
}
else{
solve3(0, 1, 2), solve3(0, 1, 3), solve3(0, 2, 3), solve3(1, 2, 3);
//cout << "ss " << ans << endl;
rp(r, 3)rep(l, 0, r - 1)ans -= solve2(l, r);
//cout << "ss " << ans << endl;
ans += solve1(0) + solve1(1) + solve1(2) + solve1(3);
printf("%lld\n", ans);
}
return 0;
}