noip8&&多校2
11.15
t1
赛时想假2.75h,敲了10k(咋做到的?不知道)
曼哈顿距离转切比雪夫距离。
直接出结论:
将每个坐标 \((x,y)\) 变作 \((x+y,x−y)\) 后,原坐标的曼哈顿距离等于新坐标的切比雪夫距离。
证明:
拆式子即可。
\[\begin{aligned}
|x_i-x_j|+|y_i-y_j|&=\max(x_i-x_j+y_i-y_j,x_j-x_i+y_i-y_j,x_i-x_j+y_j-y_i,x_j-x_i+y_j-y_i)\\
&=\max(x_i+y_i-x_j-y_j,x_j-y_j+y_i-x_i,x_i-y_i-x_j+y_j,x_j+y_j-x_i-y_i)\\
&=\max(∣(x_i+y_i)−(x_j+y_j)∣,∣(x_i−y_i)−(x_j−y_j)∣)
\end{aligned}
\]
即转为\((x+y,x-y)\)的切比雪夫距离。
然后这题就只需要维护维护\(x+y\)的最大值与最小值\(x-y\)的最大值与最小值即可。
旋转手推式子。
\((x,y)-->(-y,x)\)
对应的
\((x+y,x-y)-->(x-y,-x-y)\)
按式子转移即可。
code
t1
#include <bits/stdc++.h>
#define pir pair<int, int>
#define fi first
#define se second
using namespace std;
const int N = 2e5 + 10;
int n, m;
pir a[N];
struct tree
{
int l, r;
int mxx, mnx, mxy, mny;
int laz;
} t[N << 2];
#define lid (id << 1)
#define rid (id << 1 | 1)
inline void pushup(int id)
{
t[id].mxx = max(t[lid].mxx, t[rid].mxx);
t[id].mnx = min(t[lid].mnx, t[rid].mnx);
t[id].mxy = max(t[lid].mxy, t[rid].mxy);
t[id].mny = min(t[lid].mny, t[rid].mny);
}
inline void change(int id)
{
swap(t[id].mxx, t[id].mny);
swap(t[id].mnx, t[id].mxy);
swap(t[id].mnx, t[id].mxx);
t[id].mxy = -t[id].mxy, t[id].mny = -t[id].mny;
}
// (x,y)-->(-y,x)
inline void pushdown(int id)
{
if (!t[id].laz)
return;
for (int i = 1; i <= t[id].laz; ++i)
change(lid), change(rid);
(t[lid].laz += t[id].laz) %= 4;
(t[rid].laz += t[id].laz) %= 4;
t[id].laz = 0;
}
inline void build(int id, int l, int r)
{
t[id].l = l, t[id].r = r;
if (l == r)
{
t[id].mxx = t[id].mnx = a[l].fi + a[l].se;
t[id].mxy = t[id].mny = a[l].fi - a[l].se;
return;
}
int mid = (l + r) >> 1;
build(lid, l, mid), build(rid, mid + 1, r);
pushup(id);
}
inline void update(int id, int l, int r)
{
if (l <= t[id].l && t[id].r <= r)
{
change(id);
(t[id].laz += 1) %= 4;
return;
}
pushdown(id);
int mid = (t[id].l + t[id].r) >> 1;
if (mid >= l)
update(lid, l, r);
if (mid < r)
update(rid, l, r);
pushup(id);
}
inline tree query(int id, int l, int r)
{
if (l <= t[id].l && t[id].r <= r)
return t[id];
pushdown(id);
tree ans = {0, 0, INT_MIN, INT_MAX, INT_MIN, INT_MAX, 0};
tree ans2;
int mid = (t[id].l + t[id].r) >> 1;
if (mid >= l)
{
ans2 = query(lid, l, r);
ans.mxx = max(ans.mxx, ans2.mxx);
ans.mnx = min(ans.mnx, ans2.mnx);
ans.mxy = max(ans.mxy, ans2.mxy);
ans.mny = min(ans.mny, ans2.mny);
}
if (mid < r)
{
ans2 = query(rid, l, r);
ans.mxx = max(ans.mxx, ans2.mxx);
ans.mnx = min(ans.mnx, ans2.mnx);
ans.mxy = max(ans.mxy, ans2.mxy);
ans.mny = min(ans.mny, ans2.mny);
}
return ans;
}
inline void debug()
{
for (int i = 1; i <= n; ++i)
{
cout << "i=" << i << "\n";
tree ans = query(1, i, i);
cout << "mxx=" << ans.mxx << " mnx=" << ans.mnx << " mxy=" << ans.mxy << " mny=" << ans.mny << "\n";
}
cout << "------------------------------\n";
}
signed main()
{
freopen("fish.in", "r", stdin);
freopen("fish.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; ++i)
cin >> a[i].fi >> a[i].se;
build(1, 1, n);
int opt, l, r;
while (m--)
{
// debug();
cin >> opt >> l >> r;
if (opt == 1)
update(1, l, r);
else
{
tree ans = query(1, l, r);
cout << max(abs(ans.mxx - ans.mnx), abs(ans.mxy - ans.mny)) << "\n";
}
}
return 0;
}
/*
x=x+y
y=x-y
laz=1
(x,y)-->(-y,x) (x+y,x-y)-->(-(x-y),x+y)
*/
t2
这道题感觉很巧妙,转换成图论后很好写。
首先发现了每行选一每列选一且不能重可能会想到二分图状物(没想到怎么办?不知道,我就没想到)。
但发现数据范围是1e5级别,硬跑肯定不行。
观察,发现若我们将行和列抽象成点,每个物品向对应的行与列连边,则实际上是将该物品对应的行与列连通,边权为该物品对应价值。
连通性用并查集即可轻松维护。
于是我们可以贪心的求解。
具体的,按权值降序排列,对于每个物品,将其行与列对应点合并,同时维护siz(连通块的大小)和has(该连通块选了几个点),若\(siz_u>has_u\)则可以选该物品,答案累加上但前权值,否则跳过即可。
若不理解就感性理解一下(感觉我讲的不是很清楚,可以去搜讲解更详细的题解)。
(这个本质好像就是求最大基环树生成森林)
代码十分简单
code
t2
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
int n, r, c;
int fa[N << 1], res[N << 1];
struct node
{
int x, y, val;
} e[N];
inline bool cmp(node a, node b) { return a.val > b.val; }
inline int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); };
inline bool merge(int x, int y)
{
x = find(x), y = find(y);
if (x == y)
return 1;
fa[x] = y;
res[y] += res[x];
return 0;
}
signed main()
{
freopen("oblivious.in", "r", stdin);
freopen("oblivious.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> r >> c;
for (int i = 1; i <= r + c; ++i)
fa[i] = i, res[i] = 1;
for (int i = 1; i <= n; ++i)
cin >> e[i].x >> e[i].y >> e[i].val, e[i].y += r;
sort(e + 1, e + 1 + n, cmp);
int ans = 0;
for (int i = 1; i <= n; ++i)
{
if (!merge(e[i].x, e[i].y))
{
int x = find(e[i].x);
if (res[x])
ans += e[i].val, --res[find(x)];
}
else
{
int x = find(e[i].x);
if (res[x])
--res[x], ans += e[i].val;
}
}
cout << ans;
return 0;
}
t3
困难题(我是蒟蒻)。
首先对于区间加可以转化成差分序列上单点操作。
然后我们考虑状压。
感觉我不太会表达啊。
直接看代码?
代码里有注释。
code
t3
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 25;
const int V = 1 << 22;
const int mod = 1e9 + 7;
int n;
int a[N], b[N], jc[N], val[N];
int dp[V], g[V], siz[V];
bitset<V> vis, flag;
inline int km(int a, int b)
{
if (b < 0)
return 0;
int ans = 1;
while (b)
{
if (b & 1)
(ans *= a) %= mod;
(a *= a) %= mod;
b >>= 1;
}
return ans;
}
signed main()
{
freopen("array.in", "r", stdin);
freopen("array.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
--n;
jc[0] = val[0] = 1;
for (int i = 1; i <= n; ++i)
jc[i] = jc[i - 1] * i % mod;
for (int i = 1; i <= n + 1; ++i) //???
val[i] = 2 * km(i + 2, i - 1) % mod;
int top = 0;
for (int i = 1; i <= n; ++i)
if (a[i + 1] - a[i])
b[++top] = a[i + 1] - a[i];
n = top;
// cerr << "n=" << n << "\n";
for (int i = 1; i < (1 << n); ++i) // S
{
int sum = 0;
for (int j = 0; j < n; ++j) // 该状态下之和
if (1 & (i >> j))
sum += b[j + 1], ++siz[i];
if (!sum)
{
dp[i] = 1;
if (siz[i] == 1)
g[i] = 1;
else
g[i] = km(siz[i], siz[i] - 2);
}
}
for (int i = 1; i < (1 << n); ++i) // S
{
if (!dp[i])
continue;
bool bj = 1;
for (int j = (i - 1) & i; j; j = (j - 1) & i) // 枚举子集
if (dp[j])
{
bj = 0;
break;
}
flag[i] = bj; // 子集中是否可拆出sum和为0 yes:0 no:1
}
for (int i = 1; i < (1 << n); ++i) // S
{
if (!dp[i])
continue;
vis = 0; // 标记是否计算过
for (int j = (i - 1) & i; j; j = (j - 1) & i)
{
if (flag[j] && !vis[j])
{
if (dp[i] < dp[j] + dp[i ^ j]) // j:子集 i^j:子集的补集
dp[i] = dp[j] + dp[i ^ j], g[i] = g[j] * g[i ^ j] % mod;
else if (dp[i] == dp[j] + dp[i ^ j])
(g[i] += g[j] * g[i ^ j] % mod) %= mod;
for (int k = i ^ j; k; k = (k - 1) & (i ^ j))
vis[k] = 1;
}
}
}
int ans = 0, cnt = 0;
for (int i = 0; i < (1 << n); ++i)
ans = max(ans, dp[i]);
ans = n - ans;
if (ans == n)
cnt = 2 * km(n + 2, n - 1) % mod * jc[n] % mod;
else
{
for (int i = 0; i < (1 << n); ++i)
{
if (dp[i] == n - ans)
(cnt += g[i] * val[n - siz[i]]) %= mod;
}
(cnt *= jc[ans]) %= mod;
}
cout << ans << "\n"
<< max(cnt, 1ll) << "\n";
return 0;
}
t4
数学黑题?
我是数学低手wwwww
本文来自博客园,作者:HS_fu3,转载请注明原文链接:https://www.cnblogs.com/HS-fu3/p/19229083

浙公网安备 33010602011771号