E
位运算 拆位 并查集
一张连通图,找到一条连接1,n1,n1,n的简单路径,是的路径边权或起来最小
位运算答案最小,考虑拆位。并且可以从高位到低位贪心,贪心的看能否让每一位为0,这是对的是因为让高位为0,对于让值更小肯定是更优的。
对于第iii位,我们尝试把所有这一位位0的便都连起来,看1,n1,n1,n是否连通,如果联通说明答案的这一位可以为0。否则答案的这一位必须为1,答案加上2i2^i2i。
如果我们让第iii位位0了,那么后面在对更低位贪心的时候,就必须保持这一位为0,也就是所有这一位为1的边,后面都不能选了,用一个标志位记录一下每条边还能不能选。
int f[N];
int find(int x)
{
if (f[x] == x)
return x;
return f[x] = find(f[x]);
}
void solve()
{
int n, m;
cin >> n >> m;
vvi e;
rep(i, 1, m)
{
int u, v, w;
cin >> u >> v >> w;
e.push_back({u, v, w, 0});
}
int ans = 0;
for (int i = 30; i >= 0; i--)
{
rep(j, 1, n)
{
f[j] = j;
}
for (auto &t : e)
{
int u = t[0], v = t[1], w = t[2], used = t[3];
if (w >> i & 1)
continue;
if (used)
continue;
int fu = find(u), fv = find(v);
if (fu != fv)
{
f[fu] = fv;
}
}
if (find(1) != find(n))
{
ans += 1ll << i;
}
else
{
for (auto &t : e)
{
if (t[2] >> i & 1)
{
t[3] = 1;
}
}
}
}
cout << ans;
}
F
线段树优化dp 扫描线
一个排列,hih_ihi能转移到,下标距离他不超过RRR,值比他至少小DDD的另一个位置。从任意位置出发,问最多转移几次?
dpdpdp转移是显然的,考虑一个iii能从哪些jjj转移,显然就是在j∈[i−R,i+R]j∈[i-R,i+R]j∈[i−R,i+R],且hj>=hi+Dh_j>=h_i+Dhj>=hi+D的所有位置中,能转移过来,在他们中选个dpdpdp值最大的。
这里有两重约束,下标这个维度可以用线段树,我们让线段树上位置iii保存dpidp_idpi即可。另外的这个用数据结构不太好办。考虑用扫描线的思想,我们根据hih_ihi从大到小枚举,进行转移,枚举到iii时,才把dpi+Ddp_{i+D}dpi+D加入线段树。这样线段树内的值,就是满足hj>=hi+Dh_j>=h_i+Dhj>=hi+D的所有dpdpdp值,直接查即可。
这其实是一个类似扫描线的思想。
注意答案是移动次数,不是点数,可能需要减一。并且最优值所在终点不一定是111,最后对所有dpdpdp取min\minmin
struct Tree
{
#define ls u << 1
#define rs u << 1 | 1
struct Node
{
int l, r;
ll mx, add;
} tr[N << 2];
void pushup(int u)
{
tr[u].mx = max(tr[ls].mx, tr[rs].mx);
}
void pushdown(int u)
{
if (tr[u].add)
{
tr[ls].mx += tr[u].add * (tr[ls].r - tr[ls].l + 1);
tr[rs].mx += tr[u].add * (tr[rs].r - tr[rs].l + 1);
tr[ls].add += tr[u].add;
tr[rs].add += tr[u].add;
tr[u].add = 0;
}
}
void build(int u, int l, int r)
{
tr[u] = {l, r, 0, 0};
if (l == r)
{
tr[u].mx = 0;
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
pushup(u);
}
void modify(int u, int l, int r, int val)
{
if (tr[u].l >= l && tr[u].r <= r)
{
tr[u].mx += val * (tr[u].r - tr[u].l + 1);
tr[u].add += val;
return;
}
else
{
int mid = (tr[u].l + tr[u].r) >> 1;
pushdown(u);
if (mid >= l)
modify(ls, l, r, val);
if (r > mid)
modify(rs, l, r, val);
pushup(u);
}
}
ll query(int u, int l, int r)
{
if (l <= tr[u].l && tr[u].r <= r)
return tr[u].mx;
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (r <= mid)
return query(ls, l, r);
if (l > mid)
return query(rs, l, r);
return max(query(ls, l, r), query(rs, l, r));
}
} t;
void solve()
{
int n, D, R;
cin >> n >> D >> R;
vi h(n + 1), pos(n + 1);
rep(i, 1, n)
{
cin >> h[i];
pos[h[i]] = i;
}
t.build(1, 1, n);
vi dp(n + 1);
for (int i = n; i >= 1; i--)
{
if (i + D <= n)
{
t.modify(1, pos[i + D], pos[i + D], dp[i + D]);
}
int mx = t.query(1, max(1ll, pos[i] - R), min(n, pos[i] + R));
dp[i] = mx + 1;
}
int ans = 0;
rep(i, 1, n)
{
ans = max(ans, dp[i]);
}
cout << ans - 1;
}
3523

被折叠的 条评论
为什么被折叠?



