12.10 CW 模拟赛 T3. 循环
算法
很容易想到枚举短边断环之后 \(\mathcal{O} (P)\) 的求答案
那么这个算法还有前途吗?
可以发现, 对于每次枚举断边, 断 \((i, i + 1)\) 和 \((i - 1, i)\) 这两条边, 变化量并不大, 严格来说, 均摊复杂度 \(\mathcal{O} (P)\)
具体实现上怎么处理呢?
将断第 \(x\) 条边作为横轴, 满足每一个点对需要的边放在 \(y\) 轴, 显然的, 这就变成了一些由矩阵组合而成的图形, 注意到可以用扫描线来处理
对于每一条边, 我们预处理出其需要被使用的起始点 / 终结点, 具体的, 对于每一个要求点对 \((a,b)\) , \([1,a),[b,n]\) 中的边不选时其覆盖区间为 \([a,b)\) , 否则为 \([1,a),[b,n]\)
然后, 扫描 \(x\) 轴, 对于 \(y\) 轴, 线段树区间查询
代码
放一个 \(\rm{ZCY}\) 大佬的代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
#define int long long
int n, P, M, S, c[N], l[N], r[N], f[N], cnt;
struct seg
{
int cnt, sum;
} s[N << 2];
struct scan
{
int y_up, y_down, x, w;
} a[N << 3];
int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
void pushup(int l, int r, int rt)
{
if (s[rt].cnt)
s[rt].sum = f[r] - f[l - 1];
else
s[rt].sum = s[rt << 1].sum + s[rt << 1 | 1].sum;
}
void update(int l, int r, int rt, int L, int R, int w)
{
if (L > R)
return;
if (L <= l && R >= r)
{
s[rt].cnt += w;
pushup(l, r, rt);
return;
}
int mid = (l + r) >> 1;
if (L <= mid)
update(l, mid, rt << 1, L, R, w);
if (R > mid)
update(mid + 1, r, rt << 1 | 1, L, R, w);
pushup(l, r, rt);
}
int query(int l, int r, int rt, int L, int R)
{
if (L <= l && R >= r)
return s[rt].sum;
int mid = (l + r) >> 1, res = 0;
if (L <= mid)
res += query(l, mid, rt << 1, L, R);
if (R > mid)
res += query(mid + 1, r, rt << 1 | 1, L, R);
return res;
}
bool cmp(scan aa, scan bb)
{
return aa.x < bb.x;
}
signed main()
{
cin >> n >> P >> M >> S;
for (int i = 1; i <= n; i++)
{
S = (S * 1103515245 + 12345) % (1LL << 31);
c[i] = 1 + ((S / 10) % M);
f[i] = f[i - 1] + c[i];
}
for (int i = 1; i <= P; i++)
{
S = (S * 1103515245 + 12345) % (1LL << 31);
l[i] = ((S / 10) % n) + 1;
S = (S * 1103515245 + 12345) % (1LL << 31);
r[i] = ((S / 10) % n) + 1;
if (l[i] > r[i])
swap(l[i], r[i]);
}
for (int i = 1; i <= P; i++)
{
if (l[i] != 1)
{
update(1, n, 1, l[i], r[i] - 1, 1);
a[++cnt].x = l[i];
a[cnt].y_down = l[i], a[cnt].y_up = r[i] - 1, a[cnt].w = -1;
a[++cnt].x = l[i];
a[cnt].y_down = 1, a[cnt].y_up = l[i] - 1, a[cnt].w = 1;
a[++cnt].x = r[i];
a[cnt].y_down = 1, a[cnt].y_up = l[i] - 1, a[cnt].w = -1;
}
a[++cnt].x = l[i];
a[cnt].y_down = r[i], a[cnt].y_up = n, a[cnt].w = 1;
a[++cnt].x = r[i];
a[cnt].y_down = r[i], a[cnt].y_up = n, a[cnt].w = -1;
a[++cnt].x = r[i];
a[cnt].y_down = l[i], a[cnt].y_up = r[i] - 1, a[cnt].w = 1;
}
sort(a + 1, a + cnt + 1, cmp);
int ans = 1e18;
a[0].x = 1;
for (int i = 1; i <= cnt; i++)
{
if (a[i].x != a[i - 1].x)
ans = min(ans, s[1].sum);
update(1, n, 1, a[i].y_down, a[i].y_up, a[i].w);
}
ans = min(ans, s[1].sum);
cout << ans;
return 0;
}
总结
善于找到暴力算法为什么劣
扫描线可以处理一些均摊复杂度问题