2022NOIP A层联测18
A. 算术(a)
推一下式子,发现就是找 和 的数的个数,直接维护即可
最开始还想打线段树来着
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
ll read(){
ll x = 0; char c = getchar(); bool f = 0;
while(!isdigit(c)){f = c == '-'; c = getchar();};
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return f ? -x : x;
}
const int maxn = 1000005;
ll n, a[maxn];
ll c1, c2, c3, ans;
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
n = read();
for(int i = 1; i <= n; ++i)a[i] = read();
for(int i = n; i >= 1; --i){
if(a[i] > 1){
ans += c2 + c3;
++c1;
}else if(a[i] == 1){
ans += c1 + c2 + c3;
++c2;
}else{
ans += c1 + c2;
++c3;
}
}
printf("%lld\n",ans);
return 0;
}
B. 刷墙(b)
区间
设 表示 区间最多颜色数,转移考虑拼接两个相邻区间,即枚举大区间的断点,并加上可能存在的跨过断点的颜色贡献
设区间为 断点为 , 如果存在左端点在 , 右端点在 的颜色,那么就能多一个贡献
否则就是两侧加起来
判断是否存在这样的区间,可以二维前缀和维护
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
int x = 0; char c = getchar(); bool f = 0;
while(!isdigit(c)){f = c == '-'; c = getchar();};
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 605;
int n, lsh[maxn], p;
struct node{int l, r;}d[maxn];
int sum[maxn][maxn], f[maxn][maxn];
int get(int l1, int l2, int r1, int r2){
return sum[l2][r2] - sum[l1 - 1][r2] - sum[l2][r1 - 1] + sum[l1 - 1][r1 - 1];
}
int main(){
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
n = read();
for(int i = 1; i <= n; ++i)d[i].l = read(), d[i].r = read();
for(int i = 1; i <= n; ++i)lsh[i << 1] = d[i].l, lsh[(i << 1) - 1] = d[i].r;
sort(lsh + 1, lsh + n + n + 1); p = unique(lsh + 1, lsh + n + n + 1) - lsh - 1;
for(int i = 1; i <= n; ++i)d[i].l = lower_bound(lsh + 1, lsh + p + 1, d[i].l) - lsh;
for(int i = 1; i <= n; ++i)d[i].r = lower_bound(lsh + 1, lsh + p + 1, d[i].r) - lsh;
for(int i = 1; i <= n; ++i)++sum[d[i].l][d[i].r];
for(int i = 1; i <= p; ++i)
for(int j = 1; j <= p; ++j)
sum[i][j] = sum[i][j] - sum[i - 1][j - 1] + sum[i - 1][j] + sum[i][j - 1];
for(int len = 1; len <= p; ++len)
for(int l = 1; l <= p - len + 1; ++l){
int r = l + len - 1;
for(int k = l; k < r; ++k)f[l][r] = max(f[l][r], f[l][k] + f[k + 1][r] + (get(l, k, k + 1, r) > 0));
}
printf("%d\n",f[1][p]);
return 0;
}
C. 重复(c)
我的做法比较暴力,赛时因为常数 , 并且 模数被卡了
考虑我们找存在多少形如 的形式
暴力来讲需要枚举 ,不能接受
考虑预处理 的部分,设 表示以 为 前面的 的方案数,发现可以按长度倒叙枚举用 / 哈希表 计数,
然后考虑统计答案枚举 即可计算答案,然后发现 枚举的统计答案最后一维是后缀和的形式,提前处理即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
int x = 0; char c = getchar(); bool f = 0;
while(!isdigit(c)){f = c == '-'; c = getchar();};
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 5005;
const int mod = 1e9 + 7;
const int hmod = 1e6 + 3;
const int mod1 = 998244353;
char s[maxn];
int n;
struct HASH{
int head[1000009], net[maxn];
struct node{
int cnt, val;
}d[maxn];
int tot;
void insert(int val, int val1){
int key = 1ll * val * val1 % hmod;
for(int i = head[key]; i; i = net[i]){
if(d[i].val == val){++d[i].cnt;return;}
}
net[++tot] = head[key];
head[key] = tot;
d[tot].val = val;
d[tot].cnt = 1;
}
int query(int val, int val1){
int key = 1ll * val * val1 % hmod;
for(int i = head[key]; i; i = net[i]){
if(d[i].val == val)return d[i].cnt;
}
return 0;
}
void clear(){
for(int i = 0; i < hmod; ++i)head[i] = 0;
tot = 0;
}
}mp;
int h[maxn], bp[maxn];
int hs(int l, int r){return (h[r] - h[l - 1] * 1ll * bp[r - l + 1] % mod + mod) % mod;}
int h1[maxn], bp1[maxn];
int hs1(int l, int r){return (h1[r] - h1[l - 1] * 1ll * bp1[r - l + 1] % mod1 + mod1) % mod1;}
ll cnt[maxn][maxn];
ll ans;
int main(){
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%s",s + 1);
n = strlen(s + 1);
for(int i = 1; i <= n; ++i)h[i] = (10ll * h[i - 1] + (s[i] ^ 48)) % mod;
for(int i = 1; i <= n; ++i)h1[i] = (10ll * h1[i - 1] + (s[i] ^ 48)) % mod1;
bp[0] = 1; for(int i = 1; i <= n; ++i)bp[i] = 10ll * bp[i - 1] % mod;
bp1[0] = 1; for(int i = 1; i <= n; ++i)bp1[i] = 10ll * bp1[i - 1] % mod1;
for(int i = 1; i <= n; ++i){
mp.clear();
for(int l = n - i + 1; l >= 1; --l){
cnt[l][l + i - 1] = mp.query(hs(l, l + i - 1), hs1(l, l + i - 1));
if(l + i - 1 + i <= n)mp.insert(hs(l + i, l + i + i - 1), hs1(l + i, l + i + i - 1));
}
}
for(int i = 1; i <= n; ++i){
for(int j = n; j >= i; --j)cnt[i][j] += cnt[i][j + 1];
}
for(int l = 1; l <= n; ++l){
for(int r = l + 1; r <= n; r += 2){
int len = (r - l + 1) / 2;
if(hs(l, l + len - 1) != hs(l + len, r) || hs1(l, l + len - 1) != hs1(l + len, r))continue;
ans += cnt[l + len][r + 1];
}
}
printf("%lld\n",ans);
return 0;
}
D. 公交(d)
题意转化很妙,换根想起来简单,但是实现很细节,我是褐的。
长链剖分,数据结构,细节处理
考虑怎么求一个点的答案,考虑将一个点的点权变为子树的 size 和,即选了这个点在线路中答案就会减去size。那么最终方案一定是每个点选一条向下点权和最大的链(即长链剖分),选择长度前 k 大,的加起来。
对于求多个点的答案,注意到挪动一条边,只有O(1) 条链权值会改变,用两个 set 第一个维护前 k 大,第二个维护剩下的即可,复杂度O(nlogn)
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
int x = 0; char c = getchar(); bool f = 0;
while(!isdigit(c)){f = c == '-'; c = getchar();};
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 200005;
int n, k;
int head[maxn], tot;
struct edge{int to, net;}e[maxn << 1 | 1];
void add(int u, int v){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
}
int a[maxn];
ll sum, size[maxn], mx[maxn], cmx[maxn], dis[maxn], now, ans[maxn];
multiset<ll>s, mk;
void dfs(int x, int fa){
size[x] = a[x];
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa)continue;
dfs(v, x);
dis[x] += dis[v] + size[v];
size[x] += size[v];
if(mx[x] < mx[v] + size[v]){
cmx[x] = mx[x];
if(mx[x])s.insert(mx[x]);
mx[x] = mx[v] + size[v];
}else{
s.insert(mx[v] + size[v]);
cmx[x] = max(cmx[x], size[v] + mx[v]);
}
}
}
void del(ll val){
if(val == 0)return;
if(mk.size() && val >= *mk.begin())mk.erase(mk.lower_bound(val)), now -= val;
else s.erase(s.lower_bound(val));
while(mk.size() < k && s.size()){
mk.insert(*--s.end()); now += *--s.end();
s.erase(--s.end());
}
}
void ins(ll val){
if(val == 0)return;
if(s.size() && val < *--s.end())s.insert(val);
else mk.insert(val), now += val;
while(mk.size() > k){
s.insert(*mk.begin()); now -= *mk.begin();
mk.erase(mk.begin());
}
}
void change(int x, int fa){
ans[x] = dis[x] - now;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa)continue;
del(mx[v] + size[v]); ins(mx[v]);
ll rm = mx[v], rv = 0;
if(mx[x] == mx[v] + size[v]){
del(cmx[x]);
ins(cmx[x] + sum - size[v]);
rv = cmx[x] + sum - size[v];
}else{
del(mx[x]);
ins(mx[x] + sum - size[v]);
rv = mx[x] + sum - size[v];
}
if(rv > mx[v])cmx[v] = mx[v], mx[v] = rv;
else if(rv > cmx[v])cmx[v] = rv;
dis[v] += dis[x] - dis[v] - size[v] + sum - size[v];
change(v, x);
mx[v] = rm;
if(mx[x] == mx[v] + size[v]){
del(cmx[x] + sum - size[v]);
ins(cmx[x]);
}else{
del(mx[x] + sum - size[v]);
ins(mx[x]);
}
ins(mx[v] + size[v]);
del(mx[v]);
}
}
int main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
n = read(), k = read();
for(int i = 1; i <= n; ++i)a[i] = read(), sum += a[i];
for(int i = 1; i < n; ++i){
int u = read(), v = read();
add(u, v); add(v, u);
}
dfs(1, 0);
s.insert(mx[1]);
for(int i = 1; i <= k && s.size(); ++i){
mk.insert(*--s.end()); now += *--s.end();
s.erase(--s.end());
}
change(1, 0);
for(int i = 1; i <= n; ++i)printf("%lld ",ans[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效