DATE #:20240818
ITEM #:DOC
WEEK #:SUNDAY
DAIL #:捌月拾伍
``` 前天是小兔子,昨天是小鹿,今天是你。-- Clannad ```TAGS
<iframe border="0" frameborder="no" height="86" marginheight="0" marginwidth="0" src="//music.163.com/outchain/player?type=2&id=494858498&auto=0&height=66" width="330"></iframe> < BGM = "pure imagination--Rook1e" > < theme = oi-contest > < [NULL] > < [空] > < [空] >
T1 玩具
时间限制: 1 s 内存限制: 512 MB 测评类型: 传统型
题目描述
小T给小L买了一个玩具—毛毛虫。(不要鄙视小T的审美),毛毛虫的构造非常简单,由上下两部分组成:
上半部分是毛毛虫的身体,长度为 \(b\)。
下半部分是毛毛虫的脚,每只脚占一个单位长度,毛毛虫共有 \(L\) 只脚。
一开始毛毛虫的脚在最左边 \(L\) 个位置,身体在最左边 \(b\)个位置,最后毛毛虫的脚要在最右边 L$ 个位置,身体在最右边 \(b\) 个位置。
现在,毛毛虫要从木板的左边爬到右边。木板的长度为 \(n\),由于小T比较穷,用了很多年的木板在某些位置已经破了。毛毛虫不能将脚放在这些破的位置。
众所周知,毛毛虫是通过蠕动爬行的,因此,毛毛虫的运动分为两种操作:
- 将某一只脚向右移动若干距离,要求最后的落脚点的木板不能是破的,并且,要严格在前一只脚的左边。
- 将上半部分身体向右移动\(1\)个距离,要求移动后所有的脚仍然在身体下方。
这两种的操作均需要花费\(1\)的时间。
现在,小T希望聪明的你告诉他,毛毛虫爬到最右边(即身体的的最右一格在 n\(n\)号位置,并且所有的脚也在最右边)最少需要多少时间。
当然,有的时候,小T家的木板已经残破不堪,毛毛虫无法到达最右边,那么请输出\(“IMPOSSIBLE”\)。
问题保证最开始的 \(L\) 个位置和最后的 \(L\) 个位置木板都是好的。
输入格式
第一行空格隔开的三个数 \(L,b,n\)。
第二行连续 \(n\) 个 \(0/1\),\(0\) 表示当前位置木板是破的,\(1\) 表示当前位置木板完好。
输出格式
一行一个数,表示毛毛虫花费的最少时间。(或者“IMPOSSIBLE”\(“IMPOSSIBLE”\))
样例输入
1 3 5 11011
样例输出
5
数据范围与约定
\(20\%\) 的数据满足 \(n \le 10\)。
对于另外 \(20\%\) 的数据,满足\(L = 1\)。
对于另外 \(15\%\) 的数据,满足没有破的地板。
对于另外 \(25\%\) 的数据,满足 \(n, m \le 10000\)。
\(100\%\) 的数据满足
CODE
//2024.8.17
//by white_ice
//#115. 【2020.11.22 NOIP模拟赛 T2】玩具
#include
//#include"need.cpp"
using namespace std;
#define int long long
#define itn long long
constexpr int oo = 3e6+10;
int nl,b,n;char c[oo];int st[oo];
main(void){
//fre();
cin.tie(0)->sync_with_stdio(0);
cin >> nl >> b >> n;
cin >> c+1;
for (int i=1;i<=n;++i)
st[i] = c[i]-'0';
int r = b; int l = r-nl+1;
int cnt = nl; int cnt0 = 0;
int out = 0;
for (int i=b;i;--i){
if (cnt==0){out += nl;break;}
if (!st[i])--l;
else --cnt;
}
while (r<n){ int="" tmp="l;" while="" (r<n&&r-b+1<tmp){="" ++r;="" ++out;="" if="" (st[r]="=" 1)="" {="" ++l;while="" (st[l]="=" 0){++l;}="" }="" (l<="r-b+1){" puts("impossible");="" return="" 0;="" out+="nl;" cout="" <<="" out="" flush;="" exit(0);="" <="" code="">
直接输出impossible可以获得5分的友情分
但是正解也是非常难崩,直接贪心加模拟即可
每次记录最后一个虫脚出现的位置即可
T2 找朋友
时间限制: 1 s 内存限制: 1024 MB 测评类型: 传统型
题目描述
N 个鱼缸排成一列,按照 \(1, 2, \ldots, N\) 的顺序编号。第 \(i\) 个鱼缸里水的高度为 \(H_i\) ,保证 \(H_i < W\) 。
现在放入 \(M\) 条鱼,第 \(i\) 条鱼被放入第 \(P_i\) 个鱼缸,同时它最多能跳 \(J_i\) 单位。鱼 \(i\) 能从鱼缸 \(a\) 跳到鱼缸 \(b\) 当且仅当 \(|a - b| ≤ 1\) 且 \(J_i > W - H_a\) 。
现在每条鱼开始寻找朋友,他们从一开始所在的鱼缸开始跳,直到跳到某个鱼缸然后停下。等所有鱼都停下了,处在同一鱼缸的鱼就会相互认识。求最后相互认识的鱼的对数的最大值。
输入格式
第一行三个正整数 \(N, M, W\)(\(2 ≤ N ≤ 10^4, 1 ≤ M ≤ 200, 2 ≤ W ≤ 10^6\))。
第二行 \(N\) 个正整数 \(H_1, H_2, \ldots, H_N\)(\(1 ≤ H_i < W\))。
接下来的 \(M\) 行描述每条鱼,其中的第 \(i\) 行有两个正整数 \(P_i, J_i\)(\(1 ≤ P_i ≤ N, 1 ≤ J_i ≤ 10^6\))。
输出格式
一行一个整数表示答案。
样例输入
3 4 5
4 2 3
1 1
3 3
2 3
1 7
样例输出
3
样例说明
鱼 \(1\) 由于 \(J_1 < W - H_1\) 只能留在鱼缸 \(1\) 。
类似地,鱼 \(3\) 只能留在鱼缸 \(2\) 。
令鱼\(4\) 跳到鱼缸 \(2\) ,鱼 \(2\) 跳到鱼缸 \(2\) 。此时鱼 \(2, 3, 4\) 两两认识,对数达到 \(3\) ,可以证明这是最大值。
数据规模与约定
子任务编号
分数
额外限制
1
3
\(H_i\) 全部相同
2
7
\(N, M ≤ 20\)
3
9
\(N, M ≤ 50\)
4
14
\(M ≤ 50\)
5
67
—
时间限制: $ 1 \text{s}$
空间限制:\(1024 \text{MB}\)
CODE
//2024.8.18
//by white_ice
#include
//#include"need.cpp"
using namespace std;
#define itn long long
#define int long long
constexpr int oo = 2000006;
int n,m,w,k;
int h[oo],l[oo],r[oo];
int ned[oo];int f[1003][1003];
int st[oo],sum[oo];
main(void){
//fre();
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m >> w;
for(int i=1;i<=n;i++)cin >> k,h[i] = w - k;
for(itn x,y,i=1;i<=m;i++){
cin >> x >> y;
l[i] = r[i] = x;
while (l[i]>1&&y>h[l[i]]) l[i]--;
while (r[i]<n&&y>h[r[i]]) r[i]++;
ned[2*i-1] = l[i];ned[2*i] = r[i];
}
sort(ned+1,ned+2*m+1);
int cnt = unique(ned+1,ned+2*m+1)-ned-1;
for (itn i=1;i<=m;i++)
l[i] = lower_bound(ned+1,ned+cnt+1,l[i])-ned,
r[i] = lower_bound(ned+1,ned+cnt+1,r[i])-ned;
for (int len=1;len<=cnt;len++){
for (itn i=1;i+len-1<=cnt;i++){
int j=i+len-1;
for (itn k=1;k<=m;k++) if(i<=l[k]&&r[k]<=j){sum[l[k]]++;sum[r[k]+1]--;}
for (itn k=i;k<=j;k++){
sum[k] += sum[k-1];
int p = f[i][k-1]+f[k+1][j];
f[i][j] = max(f[i][j],sum[k]*(sum[k]-1)/2+p);
}
for (itn k=i;k<=j+1;k++)sum[k] = 0;
}
}
cout << f[1][cnt] << flush;
exit(0);
}
发现每一条鱼都有一个跳跃区间,
我们只要计算区间中重叠最多的地方即可
预处理出区间,离散化后使用区间DP求解
T3 异或
时间限制: 2 s 内存限制: 1024 MB 测评类型: 传统型
题目描述
给定一个长度为 \(n\) 的非负整数列 \(a_1, a_2, ..., a_n\) 和非负整数 \(x\),
求有多少个非空子序列 \(1 \leq b_{1} < b_{2} < \cdots < b_{k} \leq n\) , 满足对任意的 \((i, j)(1 \leq i < j \leq k)\) 都有 \(a_{b_{i}} \oplus a_{b_{j}} \geq x\)。
其中 \(\oplus\) 表示按位异或。
由于这个数可能非常大,你只需要回答这个数除以 998244353\(998244353\) 的余数即可。
输入格式
第一行两个整数 \(n, x\left(1 \leq n \leq 3 \cdot 10^{5}, 0 \leq x < 2^{60}\right)\) 。
第二行 \(n\) 个整数 \(a_{1}, a_{2}, \ldots, a_{n}\left(0 \leq a_{i} < 2^{60}\right)\) 。
输出格式
一行一个整数,表示满足条件的子序列的个数除以 \(998244353\) 的余数。
样例输入1
3 0
0 1 2
样例输出1
7
样例输入2
3 2
0 1 2
样例输出2
5
样例输入3
3 3
0 1 2
样例输出3
4
样例输入4
7 4
11 5 5 8 3 1 3
样例输出4
35
样例解释
第一组样例数据中,所有\(2^3 − 1\) 个非空子序列都合法。
第二组样例数据中,除 \([1, 2]\) 和 \([1, 2, 3]\) 外,所有非空子序列都合法。
第三组样例数据中,合法的子序列有 \([1], [2], [3]\) 和\([2, 3]\)。
数据范围
对于 \(10\%\) 的测试点,满足 \(n \leq 20\)。
对于另外 \(20\%\) 的测试点,满足 \(n \leq 5000\)。
对于另外 \(50\%\) 的测试点,满足\(n \leq 10^5\)。
对于 100%\(100\%\) 的测试点,满足 \(1 \leq n \leq 3 \cdot 10^{5}, 0 \leq x < 2^{60}, 0 \leq a_{i} < 2^{60}\)。
CODE
//2024.8.18
//by white_ice
//#120. 【2020.11.25 NOIP模拟赛 T3】异或
#include
//#include"need.cpp"
using namespace std;
#define int long long
#define itn long long
constexpr int mod = 998244353;
constexpr int oo = 3e5 + 10;
int n, x;int ned[oo];
int st[oo];int pow2[oo];
int out;
int tot = 1;
int sum[oo*60],ch[oo*60][2];
__inline void insert(int x,int v){
int u = 1;
sum[u] += v;
for (int i=59;~i;i--) {
if (x>>i&1){if (!ch[u][1])ch[u][1] = ++tot;u = ch[u][1];}
else {if (!ch[u][0])ch[u][0] = ++tot;u = ch[u][0];}
sum[u] += v;
}
}
__inline int query(int v){
int u = 1;int res = 0;
for (int i=59;~i;i--) {
if (x>>i&1)
u=ch[u][(v>>i&1)^1];
else {
res += sum[ch[u][(v>>i&1)^1]];
res %= mod;
u = ch[u][v>>i&1];
}
if (!u) return res;
}
res += sum[u];
return res;
}
main(void){
//fre();
cin.tie(0)->sync_with_stdio(0);
cin >> n >> x;
for (int i=1;i<=n;i++)cin >> st[i];
sort(st+1,st+n+1);
for (int i=1;i<=n;i++){
ned[i] = query(st[i])+1;
insert(st[i],ned[i]);
out = (out+ned[i])%mod;
}
cout << out << flush;
exit(0);
}
</code>
先引入一个引理:
结论:一个集合中任意两项异或的最小值,一定为排序后相邻两项异或的最小值。
十分简单证明
那么我们就可以在子树上DP了,
转移:
\[f_i=\sum\limits_{j=1}^{i-1}f_j[a_j \oplus a_i \le x]+1
\]这个扔到trie树上维护即可。
T4 送给好友的礼物
时间限制: 5 s 内存限制: 512 MB 测评类型: 传统型
题目背景
小 M 和小 B 是一对好朋友,她们很喜欢草莓。
题目描述
给定一棵包含 \(n\) 个节点的树 \(T\),节点从 \(1 \sim n\) 顺序编号。
小 M 和小 B 在时刻 \(0\) 都在 \(1\) 号节点。
从时刻 \(1\) 开始的每个时刻初,小 M 和小 B 都可以选择:移动到一个和自己所在节点直接相连的节点,或者停留在当前所在的节点。
树上有 \(k\) 个草莓,它们分布在 \(k\) 个不同的节点上。
小 M 和小 B 想要收集到所有的草莓,任何一个时刻末,如果小 M 或者小 B 在某一个草莓所在的节点上,那么这个草莓就被收集了。
她们不想花费太多的时间,因此你需要回答:至少在第几时刻末,小 M 和小 B 可以收集到所有的草莓,并且都回到节点 \(1\)。
输入格式
第一行两个整数 \(n\) 和 \(k\) \((1 \leq k \leq n \leq 415)\),分别表示树的节点个数和草莓的个数。
接下来 \(n-1\) 行,每行两个整数 \(u\) 和 \(v\) \((1 \leq u,v \leq n)\),表示树上有一条从 \(u\) 到 \(v\) 的边。
接下来一行 \(k\) 个互不相同的整数 \(a_1,a_2 \cdots a_k\),分别表示这 \(k\) 个草莓所在的节点。
输出格式
一行一个整数,所求答案。
输入样例 #1
7 4
1 2
2 3
2 4
1 5
5 6
6 7
3 4 5 7
输出样例 #1
6
输入样例 #2
1 1
1
输出样例 #2
0
样例解释
小 M 的路线是:1->2->3->2->4->2->1
小 B 的路线是:1->5->6->7->6->5->1
数据范围
- Subtask 1 (6 分): \(n \leq 3\)
- Subtask 2 (1 分): \(k = 1\)
- Subtask 3 (11 分): \(n \leq 7\)
- Subtask 4 (17 分):\(k \leq 20\)
- Subtask 5 (42 分):\(n \leq 90\)
- Subtask 6 (23 分):无特殊限制。
\(1 \leq k \leq n \leq 415, 1 \leq u,v \leq n\)
CODE
//2024.8.18
//by white_ice
//送给好友的礼物 | P7276
#include
//s#include"need.cpp"
using namespace std;
#define itn int
constexpr itn oo = 450;
constexpr int inf = 0x7fffffff;
int n,k;
struct nod{itn v,nxt;}st[oo<<1];int cnt,head[oo];
void add(int x,int v){st[++cnt]=(nod){v,head[x]};head[x]=cnt;}
int fa[oo],siz[oo],f[oo][oo<<1];
bool vis[oo];
bool dfs(int x){
bool fruit=0;
for(int i=head[x];i;i=st[i].nxt){
int v=st[i].v;
if(v==fa[x]||!v) continue;
fa[v]=x;
bool now=dfs(v);
if(!now) st[i].v=0;
fruit|=now;
}
return fruit|vis[x];
}
void solve(int x){
siz[x]=1; f[x][0]=0;
for(int l=head[x];l;l=st[l].nxt){
int v=st[l].v;
if(v==fa[x]||!v) continue;
solve(v);
siz[x]+=siz[v];
for(int i=(siz[x]-1)*2;i>=0;i--){
f[x][i]=f[x][i]+f[v][0]+2;
if(i>=siz[v]*2) f[x][i]=min(f[x][i],f[x][i-siz[v]*2]+f[v][(siz[v]-1)*2]);
for(int j=min((siz[v]-1)*2,i-2);j>=0;j--)
f[x][i]=min(f[x][i],f[x][i-j-2]+f[v][j]+2);
}
}
}
main(void){
//fre();
cin.tie(0)->sync_with_stdio(0);
memset(f,0x3f,sizeof(f));
cin >> n >> k;
for(int x,y,i=1;i<n;i++){ cin="">> x >> y;
add(x,y),add(y,x);
}
for(int x,i=1;i<=k;i++){cin >> x;vis[x]=1;}
dfs(1);
solve(1);
int out=inf;
for(int i=0;i<=(siz[1]-1)*2;i++)
out=min(out,max(i,f[1][i]));
cout << out << flush;
exit (0);
}
P7276 送给好友的礼物 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)在这里看吧。。。
T5 广播
时间限制: 2 s 内存限制: 512 MB 测评类型: 传统型
题目描述
C 国有 \(n\) 个城市,非常巧合的是这 \(n\) 个城市刚好构成了一棵树。C 国的国王是位日理千机的贤君,为了能及时发布自己的命令,于是决定在每个城市都建造一个信号塔,信号塔能传播信息,城市\(i\) 的信号塔的传播能力是\(val_i\): 表示如果城市\(i\) 和城市\(j\) 的距离\(dist(i, j) \leq val_i\),那么就能把信息从城市\(i\) 传到城市\(j\)。国王所在的城市为\(1\) 号城市,国王想尽可能快的让自己发布的命令在\(i\) 号城市被执行,所以他希望把命令从 \(1\) 号城市传播到 \(i\) 号城市这个过程中的传播次数尽可能少。输出从 \(1\) 号城市到 \(i\) 号城市所需的最少传播次数。
输入格式
输入的第一行有一个正整数\(n\),表示城市数量。
接下来一行有\(n\) 个正整数,表示传播距离\(val\)。
接下\(n-1\) 行每行有个正整数\(x_i,y_i\),表示一条边。
输出格式
输出一行\(n-1\) 个正整数,用空格隔开,表示答案。
样例
input
8
1 3 1 1 1 1 1 1
1 2
1 3
1 4
2 5
2 6
3 7
3 8
output
1 1 1 2 2 2 2
数据范围与提示
保证输入的边形成一棵树。
对于\(20\%\) 的数据,有\(n \leq 1000\);
对于接下来\(10\%\) 的数据,有\(n \leq 2 \times 10^5\),且是一棵完全二叉树;
对于接下来\(10\%\) 的数据,有\(n \leq 2 \times 10^5\),且是一条链;
对于接下来\(20\%\) 的数据,有\(n \leq 50000\);
对于接下来\(40\%\) 的数据,有\(n \leq 2 \times 10^5\)。
时间限制:$ 2 \text{s}$
空间限制:\(512 \text{MB}\)
是的,有T5
我们将原图转化成新图,将节点\(i\)和距离它小于\(val_i\)的所有点连边,
考虑优化建边过程,
使用点分治优化即可
最后的图刚好是个01bfs,直接跑就好
标签:oo,10,leq,2024.8,18,样例,int,毛毛虫
From: https://www.cnblogs.com/white-ice/p/18366192