DMY 周作业 47 简要题解
G
数据结构优化 DP 板。暑假的时候做过,直接离散化 + BIT 就行了。比较无聊就不说了。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 200005;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int n;
ll a[N], dp[N], lsh[N], cnt;
int lowbit(int x)
{
return (x & (-x));
}
struct BIT{
ll tr[N];
void init()
{
memset(tr, -0x3f, sizeof(tr));
}
void update(int p, ll v)
{
while(p <= cnt)
{
tr[p] = max(tr[p], v);
p += lowbit(p);
}
}
ll query(int p)
{
ll res = -inf;
while(p)
{
res = max(res, tr[p]);
p -= lowbit(p);
}
return res;
}
}tr1;
int getid(ll x)
{
return (lower_bound(lsh + 1, lsh + cnt + 1, x) - lsh);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> a[i];
a[i] += a[i - 1];
lsh[++cnt] = a[i];
}
lsh[++cnt] = 0;
sort(lsh + 1, lsh + cnt + 1);
cnt = unique(lsh + 1, lsh + cnt + 1) - lsh - 1;
tr1.init();
tr1.update(getid(0), 0);
ll premx = 0;
for(int i = 1; i <= n; i++)
{
dp[i] = tr1.query(getid(a[i])) + i;
premx = max(premx, dp[i]);
tr1.update(getid(a[i]), premx - i);
}
cout << premx;
return 0;
}
H
没见过补图的 trick,感觉这题有点牛了/bx。
一开始直接对着邻接矩阵做,转化为了交换矩阵的行列使得左上角 + 右下角形成正方形。最后的答案就是 \(\dfrac{x(x - 1)}{2}+\dfrac{y(y - 1)}{2}\),其中 \(x, y\) 分别表示黑点、白点的个数。然后就不会了。
考虑换个思路。团的特点是任意两点间都有连边,于是考虑建补图后考虑。发现此时有连边的都是颜色不同的节点,因此可以对补图做一个二分图染色,如果不是二分图则无解,否则有解。
剩下的就是平凡的了,对每个连通块求出黑点白点的个数,定义 \(dp_{i, j}\) 表示考虑考虑前 \(i\) 个点,其中有 \(j\) 个白点是否可行。这个做法有三个优化:
- 优化 \(1\):注意到我们最后只要知道黑点与白点个数之差就能推导出最后的黑点与白点各自的个数,因此状态定义可以改为 \(dp_i\) 表示黑点与白点个数之差为 \(i\) 是否可行。
- 优化 \(2\):注意到这是个值为 \(01\) 的 DP,且转移只有区间平移,可以考虑
bitset优化,转移的时间复杂度 \(O(\frac{n^2}{\omega})\),但是二分图染色的复杂度依然是 \(O(n^2)\) 的。 - 优化 \(3\):最后我们只需要优化二分图染色部分就可以了。分两部分优化:
- 第一部分:确定颜色。我们对每个连通块随机找出一个生成树,然后就能染色了。找连通块可以使用 BFS 实现,每次找出点的时候,出点集合与
vis数组的补集取交,再用bitset的_Find_first()函数求取的下一个点即可。时间复杂度 \(O(\frac{n^2}{\omega})\)。 - 第二部分:判断无解。这个就更简单了,求出每个点的颜色后,将颜色数组与出点集合取交,使用
none()或者其他的函数判断即可。时间复杂度 \(O(\frac{n^2}{\omega})\)。
- 第一部分:确定颜色。我们对每个连通块随机找出一个生成树,然后就能染色了。找连通块可以使用 BFS 实现,每次找出点的时候,出点集合与
因此,最终时间复杂度 \(O(\frac{n^2}{\omega} + m)\)。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 705, inf = 0x3f3f3f3f, PY = 705, SZ = 1410;
int n, m, ans = inf, a[N], col[N], vt;
bitset<N> mat[N], vis, colbs, lmt;
bitset<SZ> dp;
void dfs(int u)
{
vt += (col[u] == 1 ? 1 : -1);
colbs[u] = (col[u] == 1);
vis[u] = 1;
while(1)
{
int v = ((~vis) & mat[u])._Find_first();
if(v > n) break;
int toc = 3 - col[u];
col[v] = toc;
dfs(v);
}
}
int main()
{
//freopen("sample.in", "r", stdin);
//freopen("sample.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i++) mat[i][i] = lmt[i] = 1;
for(int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
mat[u][v] = mat[v][u] = 1;
}
for(int i = 1; i <= n; i++)
{
mat[i].flip();
mat[i] &= lmt;
}
memset(col, -1, sizeof(col));
dp[PY] = 1;
for(int i = 1; i <= n; i++)
{
if(col[i] != -1) continue;
col[i] = 1;
vt = 0;
dfs(i);
vt = abs(vt);
dp = ((dp >> vt) | (dp << vt));
}
for(int i = 1; i <= n; i++)
{
if(colbs[i] == 0)
{
if(!((colbs & mat[i]) == mat[i]))
{
cout << "-1";
return 0;
}
}
else
{
if(!(((~colbs) & mat[i]) == mat[i]))
{
cout << "-1";
return 0;
}
}
}
for(int i = -n; i <= n; i++)
{
if((n & 1) != (i & 1)) continue;
if(!dp[i + PY]) continue;
int x = (n + i) / 2, y = (n - i) / 2;
ans = min(ans, x * (x - 1) / 2 + y * (y - 1) / 2);
}
cout << ans;
return 0;
}

浙公网安备 33010602011771号