还行吧,但我实在太菜了,水题都不会做
A
Description
数轴上 $n$ 个点,求离最近的点的距离恰好为 $d$ 的点的数目。
Solution
扫一遍,我直接用 set
去重()
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <set>
using std::cin;
using std::cout;
using std::endl;
const int maxn = 110;
long long a[maxn], n, d;
int main()
{
cin >> n >> d;
for (int i = 1; i <= n; i++)
cin >> a[i];
std::set<long long> S;
a[0] = -5e9, a[n + 1] = 5e9;
for (int i = 1; i <= n; i++)
{
int nxt = a[i] + d;
if (a[i + 1] - nxt >= d)
S.insert(nxt);
nxt = a[i] - d;
if (nxt - a[i - 1] >= d)
S.insert(nxt);
}
cout << S.size() << endl;
}
B
Description
一个长度为 $n$ 的 $01$ 串,给出 $m$ 个区间,每个区间权值是区间里 $1$ 的数目乘 $0$ 的数目。输出总权值最大的串。
Solution
和同近积大,直接 $0$ 和 $1$ 交替输出就行。
Code
n, m = map(int, input().split())
for i in range(n):
if i % 2 == 0:
print(0, end='')
else:
print(1, end='')
C
Description
给你 $n$ 个数,从每一个数起向后找一个数,这两个数组成一个数对,求一共有多少个数对。(去掉重复的)
Solution
一个数最优答案肯定在最左面,我们开个桶然后倒着更新答案即可。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <set>
using std::cin;
using std::cout;
using std::endl;
const int maxn = 1e5 + 10;
int a[maxn], n, b[maxn], ans[maxn];
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
int sum = 0;
for (int i = n; i >= 1; i--)
{
ans[a[i]] = sum;
sum -= b[a[i]];
b[a[i]] = 1, sum++;
}
long long res = 0;
for (int i = 1; i <= 1e5; i++)
res += ans[i];
cout << res << endl;
}
D
Description
定义一种矩阵是菱形矩阵,其大小是 $n\times m$;满足以下条件:有且只有一个格子的值是 $0$,其他格子的值是到值为 $0$ 的格子的曼哈顿距离。
给出 $t$ 个数,问这些数能不能构造出一个菱形矩阵。如果可以,输出其 $n,m$,以及值为 $0$ 的格子的坐标。
$t\leq10^6$。
Solution
首先发现矩阵是可以旋转的,这肯定不影响。
如果矩阵无限大,那每个数的数量是 $4\times n$。
手玩一下样例可以发现第一个不符合上面要求的数(即出到矩阵外了)一定是值为 $0$ 的数的横坐标或纵坐标。
这样我们就确定了 $x$。
证明不写了,懒,挺显然的。
我们可以枚举 $n$,同时也就确定了 $m$。想一想怎么确定 $y$——找找奇怪的性质。
发现最大的数一定在一个角上,那随便算一下就知道 $y=n+m-mx$,$mx$ 是给出的最大的数。
发现我们现在时间复杂度还很充裕,且已经知道了 $x,y,n,m$,只需要 check 一下,那怎么 check 呢?当然是暴力了!()()()
时间复杂度 $O(t)$ 乘上 $t$ 的因数个数,这玩意显然能过。(因为很难跑满)
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <set>
#include <cmath>
using std::cin;
using std::cout;
using std::endl;
const int maxn = 1e6 + 10;
int cnt[maxn], n, t, m, x, y, mx, tot[maxn];
bool check(int n, int m, int x, int y)
{
for (int i = 0; i <= mx + 5; i++)
tot[i] = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
tot[abs(i - x) + abs(j - y)]++;
for (int i = 0; i <= mx; i++)
if (cnt[i] != tot[i])
return false;
return true;
}
int main()
{
std::ios::sync_with_stdio(false);
cin >> t;
for (int i = 0, p; i < t; i++)
cin >> p, cnt[p]++, mx = std::max(mx, p);
if (cnt[0] != 1)
{
cout << -1 << endl;
return 0;
}
for (int i = 1; i <= t; i++)
if (cnt[i] != i * 4)
{
x = i;
break;
}
for (n = 1; n <= t; n++)
{
if (t % n)
continue;
m = t / n;
y = m + n - x - mx;
if (y > 0 && check(n, m, x, y))
{
cout << n << " " << m << endl << x << " " << y << endl;
return 0;
}
}
cout << -1 << endl;
}
E
Description
给出一个 $n$ 个结点的带权树和 $k$,找出一条点数不大于 $k$ 的路径,使得路径外的点到路径的距离的最大值最小。求这个最小的最大值。$n\leq10^5$。
Solution
和树网的核一样。甚至更简单
树上路径、最小、最大,去想一想直径。
能证明这条路径一定在任意一条直径上。
于是对于一条路径分两种情况。到路径距离最大的点可能是直径的两个段点之一,也可能在路径上某个点的子树内。
对前者直接双指针即可,后者搜一遍就行。
时间复杂度 $O(n)$。
Code
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
using std::cin;
using std::cout;
using std::endl;
const int maxn = 1e5 + 10;
struct edge
{
int y, w;
};
std::vector<edge> e[maxn];
inline void add(int x, int y, int w) { e[x].push_back({y, w}), e[y].push_back({x, w}); }
int n, k;
int dis[maxn], father[maxn], ind[maxn], dep[maxn];
void dfs(int x, int fa)
{
father[x] = fa, dep[x] = dep[fa] + 1;
for (auto k : e[x])
{
if (k.y == fa || ind[k.y])
continue;
int v = k.y;
dis[v] = dis[x] + k.w;
dfs(v, x);
}
}
int find_root()
{
int res = 0;
for (int i = 1; i <= n; i++)
if (dis[i] > dis[res])
res = i;
return res;
}
int main()
{
std::ios::sync_with_stdio(false);
cin >> n >> k;
for (int i = 1, x, w, y; i < n; i++)
cin >> x >> y >> w, add(x, y, w);
dfs(1, 0);
int root = find_root();
dis[root] = 0;
dfs(root, 0);
root = find_root();
int ans = 1 << 30;
if (n == 1)
{
puts("0");
return 0;
}
for (int i = root, j = root; i; i = father[i])
{
while (father[j] && dep[i] - dep[j] < k - 1)
j = father[j];
ans = std::min(ans, std::max(dis[j], dis[root] - dis[i]));
}
for (int p = root; p; p = father[p])
ind[p] = 1, dis[p] = 0;
for (int p = root; p; p = father[p])
dfs(p, father[p]);
for (int i = 1; i <= n; i++)
ans = std::max(ans, dis[i]);
cout << ans << endl;
}
F
Description
有一个长度为 $n$ 的序列,有两种操作。
-
操作一:将 $a_i$ 修改为 $y$;
-
操作二:给出 $l,r$,求 $[l,r]$ 内有几个子区间内所有数按位或起来的结果大于等于 $X$($X$ 在题目一开始给出,为定值)。
$n,m\leq10^5$。
Solution
难,我看了题解。
先考虑如果只有一次询问怎么办。
能(能吗?)想到一个分治算法:把 $[l,r]$ 的询问拆成 $[l,mid],[mid+1,r]$,求出解,然后合并这两个区间。
具体怎么得出大区间的解呢?我们联想区间最长连续子段和的做法,求出每个区间的前缀或和后缀或。
这样我们可以用双指针得到两个区间合并后的解。
单点修改,区间查询,显然我们需要线段树来维护这个东西。
那怎么把上面这个做法推广到线段树上呢?好像直接做不太行,我们显然需要看一看或运算的性质。
一个数或另一个数之后最少只变了 $1$ 位,且不会变回去,也就是说每个区间前缀/后缀或应该是分成至多 $\log a_i$ 段的,我们用 vector
维护这个值,就能做到 $O(\log a_i)$ 合并了。
那我们用线段树维护,把得到的这 $\log n$ 个区间依次合并起来就行。
这时候发现时间复杂度是 $O(m\log n\log a_i)$。
后来我双指针写挂了,一怒之下把双指针换成了暴力合并,复杂度是 $O(m\log n\log^2a_i)$,也能轻松通过。
注意位运算的优先级。
Code
建议把所有函数规划好再写,然后想一些简单的方法来简化代码。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::cerr;
namespace IO
{
const int mxsiz = 1 << 20;
char inbuf[mxsiz], *p1, *p2;
char outbuf[mxsiz], *op = outbuf;
struct endio
{
endio(){};
~endio() { fwrite(outbuf, 1, op - outbuf, stdout); }
} useless_var;
inline char gc() { return p1 == p2 && (p2 = (p1 = inbuf) + fread(inbuf, 1, mxsiz, stdin), p1 == p2) ? EOF : *p1++; }
#define isdigit(x) (x >= '0' && x <= '9') // 防止忘记打 <cctype> 头文件
inline int read()
{
int x = 0, f = 1;
char ch = gc();
for (; !isdigit(ch); ch = gc())
if (ch == '-')
f = -1;
for (; isdigit(ch); ch = gc())
x = x * 10 + ch - '0';
return x * f;
}
template <typename T>
inline void read(T &x)
{
x = 0;
int f = 1;
char ch = gc();
for (; !isdigit(ch); ch = gc())
if (ch == '-')
f = -1;
for (; isdigit(ch); ch = gc())
x = x * 10 + ch - '0';
x *= f;
}
#undef isdigit
inline bool ischar(char x)
{
return x >= 'A' && x <= 'z';
}
inline char readchar()
{
char ch = gc();
while (!ischar(ch))
ch = gc();
return ch;
}
inline void push(char ch)
{
if (op - outbuf == mxsiz)
fwrite(outbuf, 1, mxsiz, stdout), op = outbuf;
*op++ = ch;
}
template <typename T>
inline void work_wt(T x)
{
if (x > 9)
work_wt(x / 10);
push(x % 10 + '0');
}
template <typename T>
inline void write(T x)
{
if (x < 0)
x = -x, push('-');
work_wt(x);
}
inline void writestr(char *s)
{
int n = strlen(s);
for (int i = 0; i < n; i++)
push(s[i]);
}
inline void endln() { push('\n'); }
inline void space() { push(' '); }
template <typename T>
inline void writeln(T x) { write(x), endln(); }
}
using namespace IO;
const int maxn = 1e5 + 10;
typedef long long ll;
int n, X, m;
int a[maxn];
struct seg
{
int val, len;
void p() { write(val), space(), write(len), space(), endln(); }
};
struct node
{
std::vector<seg> l, r;
ll ans;
void p()
{
writestr("l :\n");
for (auto k : l)
k.p();
writestr("r :\n");
for (auto k : r)
k.p();
writeln(ans);
}
} t[maxn * 4];
inline int ls(int x) { return x * 2; }
inline int rs(int x) { return x * 2 + 1; }
node merge(const node &ls, const node &rs)
{
node k;
k.l = ls.l;
for (auto p : rs.l)
{
if ((k.l.back().val | p.val) == k.l.back().val)
k.l.back().len += p.len;
else
k.l.push_back({k.l.back().val | p.val, p.len});
}
k.r = rs.r;
for (auto p : ls.r)
{
if ((k.r.back().val | p.val) == k.r.back().val)
{
k.r.back().len += p.len;
}
else
k.r.push_back({k.r.back().val | p.val, p.len});
}
k.ans = ls.ans + rs.ans;
// 原先的双指针
/*
for (int i = 0, j = (int)rs.l.size() - 1; i < (int)ls.r.size(); i++)
{
while (j && ls.r[i].val | rs.l[j].val >= X)
j--;
if (j >= 0)
k.ans += rs.l[j].sum * ls.r[i].len;
} */
for (auto i : ls.r)
for (auto j : rs.l)
if ((i.val | j.val) >= X)
k.ans += (ll)i.len * j.len;
return k;
}
void modify(int l, int r, int x, int v, int k)
{
if (l == r)
{
t[k].ans = v >= X, t[k].l.clear(), t[k].r.clear();
t[k].l.push_back({v, 1}), t[k].r.push_back({v, 1});
return;
}
int mid = (l + r) / 2;
if (x <= mid)
modify(l, mid, x, v, ls(k));
else
modify(mid + 1, r, x, v, rs(k));
t[k] = merge(t[ls(k)], t[rs(k)]);
}
node query(int l, int r, int x, int y, int k)
{
if (l == x && r == y)
return t[k];
int mid = (l + r) / 2;
if (y <= mid)
return query(l, mid, x, y, ls(k));
if (x > mid)
return query(mid + 1, r, x, y, rs(k));
return merge(query(l, mid, x, mid, ls(k)), query(mid + 1, r, mid + 1, y, rs(k)));
}
void build(int l, int r, int k)
{
if (l == r)
{
t[k].ans = a[l] >= X, t[k].l.clear(), t[k].r.clear();
t[k].l.push_back({a[l], 1}), t[k].r.push_back({a[l], 1});
return;
}
int mid = (l + r) / 2;
build(l, mid, ls(k));
build(mid + 1, r, rs(k));
t[k] = merge(t[ls(k)], t[rs(k)]);
}
int main()
{
n = read(), m = read(), X = read();
for (int i = 1; i <= n; i++)
a[i] = read();
build(1, n, 1);
while (m--)
{
int opt = read(), x = read(), y = read();
if (opt == 1)
modify(1, n, x, y, 1);
else
writeln(query(1, n, x, y, 1).ans);
}
}