2022NOIP A层联测18

A. 算术(a)

推一下式子,发现就是找 >=1<=1 的数的个数,直接维护即可

最开始还想打线段树来着

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)

区间 DP

fl,r 表示 [l,r] 区间最多颜色数,转移考虑拼接两个相邻区间,即枚举大区间的断点,并加上可能存在的跨过断点的颜色贡献

设区间为 [l,r] 断点为 k, 如果存在左端点在 [l,k], 右端点在 [k+1,r] 的颜色,那么就能多一个贡献

否则就是两侧加起来

判断是否存在这样的区间,可以二维前缀和维护

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)

我的做法比较暴力,赛时因为常数 TLE, 并且 hash 模数被卡了

考虑我们找存在多少形如 aabcab 的形式

暴力来讲需要枚举 a,bc,不能接受

考虑预处理 abcab的部分,设 cntl,r 表示以 [l,r]c 前面的 ab 的方案数,发现可以按长度倒叙枚举用 map / 哈希表 计数,

然后考虑统计答案枚举 aab 即可计算答案,然后发现 n3 枚举的统计答案最后一维是后缀和的形式,提前处理即可 n2

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;
}
posted @   Chen_jr  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示