1012死亡之组
题意:给出一个4的倍数的n,以及n个队伍的实力,问什么取法能使得1号所在的小组不是死亡之组,即球队里至少有3个队伍实力小于L且最大的和最小的差不超过D。
思路:排序然后取第一个小组后尽可能多的取满3个小于L的小组,最后一个尽可能取最大的满足第二个条件,模拟一下即可。
1001深度自同构
题意:找出x点构成的满足题目条件的森林有几个,深度自同构指森林里深度相同的点的度相同
思路:推几组找规律即可,发现x越大,它可以分成多种前面有的小x,把小x的贡献都加上即可。
1011抓拍
题意:给出n个点,每个点有移动方向,移动方向只可能是上下左右四个方向,然后每个点移动速度是相同的,要求是在任意非负时间上找到一个周长最小的矩形能包括所有的点。
思路:题目要求周长最小的矩形,看起来点很多,我们很难找到怎么围这个矩形,但是稍微思考一下会发现,其实就是找任意时刻上下左右四个跑的最远的点就行,我们可以枚举时间,然后枚举每个点在这个时间对应的位置,找到四个最远的点直接算。但是这样时间复杂度太大了,我们要考虑优化。再仔细读题会发现,每个时间对于任意一个长度的变化只可能是-2 -1 0 1 2其中的一中,既有正数又有负数,那就说明周长曲线应该是一个u行曲线,因此我们不如三分时间,条件就是去逼近最小的中心即可。
1007 单峰数列
题意:给出一个数列以及五种操作,根据对应的操作要求输出即可。
思路:看到段落查询段落修改就应该想到这是一道非常标准的线段树的题目了,问题是改如何维护这个线段树来尽可能的降低时间复杂度,首先直接维护数列是不行的,因为数列很难去维护一段的单调性相同性以及单峰性。思考有什么数据结构可以简单快捷的维护相同,单调。会发现差分数组非常满足这个条件,如果一段差分数组的值都是0,那么说明这一段肯定都是相同的,因为变化量为0,如果一段差分数组的值都是正数,那么一个个累加过去每个位置肯定也是递增的,同理递减也是如此,因此线段树维护一个差分数组就行。而且差分数组还有一个好处(?也可能是坏处,但是写起来是好处,因为更加简单了)就是只需要单点修改就行,因此不需要考虑痛苦的lazy标记,但是因此的复杂度也比较高,不过在这题里面还可以忍受。具体实现比较复杂,请看我的代码,代码里有注释方便理解。
代码
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<queue>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
#define ll long long
const ll N = 1e5 + 10;
const ll mod = 998244353;
ll arr[N] = { 0 };
ll brr[N] = { 0 };
struct tree
{
ll l;
ll r;
ll sum;
ll up;
ll down;
}tr[N<<4];
void pushup(ll p)
{
ll l = p << 1;
ll r = l + 1;
if (tr[l].sum == 0 && tr[r].sum == 0) tr[p].sum = 0;
else tr[p].sum = 1;
if (tr[l].up > 0 && tr[r].up > 0) tr[p].up = 1;
else tr[p].up = 0;
if (tr[l].down < 0 && tr[r].down < 0) tr[p].down = -1;
else tr[p].down = 0;
return;
}
void build(ll p, ll l, ll r)
{
if (l == r)
{
tr[p] = { l,l,brr[l],brr[l],brr[l] };
return;
}
tr[p].l = l;
tr[p].r = r;
ll mid = (l + r) / 2;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
pushup(p);
return;
}
void modify(ll p, ll pos, ll v)
{
if (tr[p].l == pos && tr[p].r == pos)
{
tr[p].sum += v;
tr[p].up += v;
tr[p].down += v;
return;
}
ll mid = (tr[p].l + tr[p].r) / 2;
if (pos<=mid)modify(p * 2, pos, v);
else modify(p * 2 + 1, pos, v);
pushup(p);
return;
}
ll query(ll p, ll l, ll r, ll op)
{
if (l <= tr[p].l && r >= tr[p].r)
{
if (op == 2) return tr[p].sum;
else if (op == 3) return tr[p].up;
else if (op == 4) return tr[p].down;
}
ll mid = (tr[p].l + tr[p].r) / 2;
ll f = 1;
if (op == 2)
{
if (l <= mid)
{
if (query(p * 2, l, r, op) != 0) f = 0;
}
if (r > mid)
{
if (query(p * 2 + 1, l, r, op) != 0)f = 0;
}
if (f) return 0;
else return 1;
}
else if (op == 3)
{
if (l <= mid)
{
if (query(p * 2, l, r, op) <= 0) f = 0;
}
if (r > mid)
{
if (query(p * 2 + 1, l, r, op) <= 0)f = 0;
}
if (f) return 1;
else return 0;
}
else
{
if (l <= mid)
{
if (query(p * 2, l, r, op) >= 0) f = 0;
}
if (r > mid)
{
if (query(p * 2 + 1, l, r, op) >= 0)f = 0;
}
if (f) return -1;
else return 0;
}
}
void solve()
{
ll n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> arr[i];
brr[i] = arr[i] - arr[i - 1];
}
build(1, 1, n);
ll q;
cin >> q;
for (int i = 1; i <= q; i++)
{
ll op,l,r;
cin >> op >> l >> r;
if (op == 1)
{
ll v;
cin >> v;
modify(1, l, v);
if (r+1<=n)modify(1, r + 1, -v);
}
else if (op == 2)
{
if (query(1, l+1, r, op) == 0) cout << 1 << endl;
else cout << 0 << endl;
}
else if (op == 3)
{
if (query(1, l+1, r, op) > 0 || l == r) cout << 1 << endl;
else cout << 0 << endl;
}
else if (op == 4)
{
if (query(1, l+1, r, op) < 0 || l == r)cout << 1 << endl;
else cout << 0 << endl;
}
else
{
ll ans = 0;
ll nl = l+1;
ll nr = r;
while (nl <= nr)
{
ll mid = (nl + nr) / 2;
if (query(1, nl, mid, 3)) ans=max(ans,mid),nl=mid+1;
else nr = mid - 1;
}
if (query(1, l + 1, ans, 3) <= 0)
{
cout << 0 << endl;
continue;
}
if (ans == r)
{
cout << 0 << endl;
continue;
}
if (query(1, ans + 1, r, 4) >= 0)
{
cout << 0 << endl;
}
else
{
cout << 1 << endl;
}
}
}
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
ll t;
t = 1;
while (t--)
{
solve();
}
}
1008 比特跳跃
题意:给出一个无向图,问从1号城市到每个城市所需要的最少时间,除了按边的要求耗费t的时间从u到v外,还有一个特殊的操作叫比特跳跃,从任意一个x到y,耗费一个k*(x|y)的值。
思路:首先考虑没有特殊操作的情况,发现是很标准的最短路算法,跑一遍djs可以轻松的处理,然后在这个基础上加上特殊操作比特跳跃,发现任意操作非常复杂,但是我们可以根据按位与去化简这个操作,首先就是对于任意的奇数序号点,我们发现奇数序号点之间通过比特跳跃到的最优解是直接从1跳跃到希望点,因为我们从x跳跃到y的代价是先从1正常走到x或者从1跳跃到x再跳跃到y,但是不管选择哪一种,里面至少都有一个k*(x|y)的代价。而x|y的代价肯定>=y|1的值,但是如果我们直接从1跳到目标点,代价肯定是k*(x)比上面两种选择都更优的。那为什么偶数不能这样考虑呢,因为对偶数来说,从1直接跳到偶数结点,会有一个k*(x+1)的代价,而这个+1视k的大小就会导致从x跳跃到y的代价x|y可能更优化,因此我们要考虑x|y就等于两者之间较大值的点之间的跳跃能否有更优解,即二进制下,较小数是较大数的一部分,这样跳跃就是较大数的消耗,不会有那个+1的值,看看这种情况有没有可能更优,优化完后再跑一遍djs即可,因为优化后可能导致某些点的最短路值变小,因此此时还需要再跑一遍djs。
代码:
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<queue>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
#define ll long long
#define int long long
#define PII pair<int,int>
#define x first
#define y second
const ll N = 1e5 + 10;
const ll mod = 998244353;
ll n, m, k;
vector<vector<PII>> edge(n + 1);
ll vis[N] = { 0 };
ll minans[N] = { 0 };
ll ans[N] = { 0 };
ll lowbit(ll a)
{
return (a & (-a));
}
void djs()
{
priority_queue<PII, vector<PII>, greater<PII>> q;
ans[0] = ans[1] = 0;
q.push({ 0,1 });
while (q.size())
{
int u = q.top().y;
q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (int i = 0; i < edge[u].size(); i++)
{
int v = edge[u][i].x;
int val = edge[u][i].y;
if (ans[v] > ans[u] + val)
{
ans[v] = ans[u] + val;
q.push({ ans[v],v });
}
}
}
for (int i = 0; i <= n; ++i) minans[i] = ans[i], vis[i] = 0;
for (int i = 2; i <= n; ++i) {
if (i != lowbit(i))
for (int j = 0; (1 << j) <= n; ++j)
if (i & (1 << j)) minans[i] = min(minans[i], minans[i ^ (1 << j)]);
if (minans[i] + 1ll * k * i < ans[i]) ans[i] = minans[i] + 1ll * k * i;
}
for (int i = 1; i <= n; i++) vis[i] = 0, q.push({ ans[i],i });
while (q.size())
{
int u = q.top().y;
q.pop();
if (vis[u])continue;
vis[u] = 1;
for (int i = 0; i < edge[u].size(); i++)
{
int v = edge[u][i].x;
int val = edge[u][i].y;
if (ans[v] > ans[u] + val)
{
ans[v] = ans[u] + val;
q.push({ ans[v],v });
}
}
}
return;
}
void solve()
{
cin >> n >> m >> k;
edge = vector<vector<PII>>(n + 1);
for (int i = 1; i <= m; i++)
{
ll a, b, t;
cin >> a >> b >> t;
edge[a].push_back({ b,t });
edge[b].push_back({ a,t });
}
for (int i = 1; i <= n; i++)
{
vis[i] = 0;
ans[i] = 1e15;
if (i != 1)
{
edge[1].push_back({ i,k * (i | (ll)1) });
}
}
djs();
for (int i = 2; i <= n; i++)
{
cout << ans[i];
if (i == n)
{
cout << endl;
}
else
{
cout << ' ';
}
}
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
ll t;
cin >> t;
while (t--)
{
solve();
}
}