2023牛客寒假集训2
A-Tokitsukaze and a+b=n (easy)[暴力]
A-Tokitsukaze and a+b=n (easy)_2023牛客寒假算法基础集训营2 (nowcoder.com)
Tokitsukaze 有一个整数 \(n\), 以及 $2 $个区间 \([L_1,R_1],[L_2,R_2]\)。
她想知道有多少种选法,满足:从第一个区间选择一个整数 \(a\) (\(L_1 \leq a \leq R_1\)),从第二个区间选择一个整数 \(b\) (\(L_2 \leq b \leq R_2\)),使得 \(a+b=n\)。
对于两种选法,若$a, b $中有任意一个数不同,则算作不同的选法。
就是在第一个区间遍历一遍看看有多少个对应的点落在第二个区间里面
in
4
5
3 5
1 4
100000
1 100000
1 100000
200000
1 100000
1 100000
114
514 1919
8 10
out
2
99999
1
0
key code
int l,r;
cin>>n;
cin>>l>>r;
cin>>m>>k;
int ans=0;
up(l,r){
int res=n-o;
if(res<=k&&res>=m)ans++;
}
cout<<ans<<endl;
B-Tokitsukaze and a+b=n (medium)[交集]
B-Tokitsukaze and a+b=n (medium)_2023牛客寒假算法基础集训营2 (nowcoder.com)
easy 与 medium 的唯一区别是输入的数据范围。
Tokitsukaze 有一个整数 \(n\), 以及 $2 $个区间 \([L_1,R_1],[L_2,R_2]\)。
她想知道有多少种选法,满足:从第一个区间选择一个整数 \(a\) (\(L_1 \leq a \leq R_1\)),从第二个区间选择一个整数 \(b\) (\(L_2 \leq b \leq R_2\)),使得 \(a+b=n\)。
对于两种选法,若$a, b $中有任意一个数不同,则算作不同的选法。
易知\([L_1,R_1]\)对应的区间是\([n-R_1,n-L_1]\)
然后就是看\([L_2,R_2]\)和\([n−R1,n−L1]\)有多少交集
如何求两个区间的交集长度?
区间1\([a,b]\)
区间2\([c,d]\)
重叠长度 \(=\)
max(0,min(b,d)-max(a,c)+1)
in
4
5
3 5
1 4
100000
1 100000
1 100000
200000
1 100000
1 100000
114
514 1919
8 10
out
2
99999
1
0
key code
cin>>n;
cin>>a>>b>>c>>d;
int ans=0;
ans=max(0ll,min(b,n-c)-max(a,n-d)+1);
puts(ans);
C-Tokitsukaze and a+b=n (hard)[配对]
C-Tokitsukaze and a+b=n (hard)_2023牛客寒假算法基础集训营2 (nowcoder.com)
Tokitsukaze 有一个整数 \(n\), 以及 \(m\)个区间 \([L,R]\)。
她想知道有多少种选法,满足:从一个区间选择一个整数 \(a\) (\(L_1 \leq a \leq R_1\)),从另一个区间选择一个整数 \(b\) (\(L_2 \leq b \leq R_2\)),使得 \(a+b=n\)。
对于两种选法,若$i,j,a, b $中有任意一个数不同,则算作不同的选法。
由于答案可能很大,请输出对 \(998244353\) 取模后的结果。
我们可以把有数的地方都填上数
区间的大小是\(2*10^5\) 所以我们可以枚举区间里的每一个数的个数并寻找其配对的个数
如果暴力记录每一个数的次数会 \(TLE\) 所以就需要用到区间计数
cin>>l>>r;
a[l]--;
a[r+1]++;
最后再
up(1,n){
a[o]=(a[o-1]+a[o]%mod);
}
in
5 3
1 3
2 4
3 5
out
12
key code
const int N=4e5+10;
const int mod=998244353;
int n,m;
int a[N],b[N];
void solve(){
//try it again.
cin>>n>>m;
int ans=0;
up(1,m){
int l,r;
cin>>l>>r;
a[l]--;
a[r+1]++;
ans-=max(0ll,min(r,n-l)-max(l,n-r)+1ll);
}
up(1,n){
a[o]=(a[o-1]+a[o]%mod);
}
up(1,n-1){
ans+=((a[n-o]*a[o])%mod);
}
cout<<(ans+mod)%mod<<endl;
}
D-Tokitsukaze and Energy Tree[递推]
D-Tokitsukaze and Energy Tree_2023牛客寒假算法基础集训营2 (nowcoder.com)
Tokitsukaze 有 $n $ 个节点的有根能量树,根为 \(1\)。最开始,树上每个节点的能量都是 \(0\)。
现在 Tokitsukaze 有 $n $ 个能量球,第 $i $ 个能量球拥有 $ v_i$ 能量。她想把这 \(n\) 个能量球分别放置在能量树的每个节点上,使能量树的每个节点都恰好有一个能量球。
\(Tokitsukaze\) 每次只能放置一个能量球,所以她将进行 \(n\) 次操作。每一次操作,她会选择一个能量球,再选择一个没有能量球的能量树节点 $ x$,把刚刚选择的能量球放置在节点 $x $ 上。在这之后,\(Tokitsukaze\) 能获得以 \(x\) 为根的子树中的所有能量球的能量 (包括节点 $x $ 的能量球能量)。
在放置完所有能量球后,Tokitsukaze 可能获得的总能量最多是多少?
通过观察题意我们可以知道每一个子节点都会比父节点多计数一次
由此便形成了一个递推关系
我们让能量大的水晶球和选取次数多的节点配对
由此便可推出答案
in
5
1 1 3 3
1 1 2 2 3
out
22
key code
const int N=2e5+10;
int n,m,k,a[N],b[N],p[N];
void solve(){
cin>>n;
b[1]=1;
fup(i,2,n){int x;cin>>x;b[i]=b[x]+1;}
up(1,n)cin>>a[o];
sort(a+1,a+1+n);
sort(b+1,b+1+n);
int tt=0;
up(1,n)tt+=b[o]*a[o];
cout<<tt<<endl;
}
E-Tokitsukaze and Function[数学]
E-Tokitsukaze and Function_2023牛客寒假算法基础集训营2 (nowcoder.com)
Tokitsukaze 有一个函数
\(f(x)=\lfloor \frac{n}{x} \rfloor+x-1\)
她想在区间\([L,R]\) 中找到一个最小的整数 t,使函数\(f(t)\)的值最小。
in
11
9 1 1
9 1 2
9 1 3
9 1 4
9 1 5
9 5 6
9 5 7
9 5 8
9 5 9
1145141919810 114514 1919810
8 1 8
out
1
2
2
2
2
5
5
5
5
1069123
3
首先先找出来最低点
在 \(\sqrt{n}\) \(,\sqrt{n}+1,\) \(l,r\) 之间找
\(f(x)=\lfloor \frac{n}{x} \rfloor+x-1\)这个东西显然他的曲线就类似于开口向上的二次函数的曲线不过有很多段向下倾斜的锯齿状,如果有很多个最低点的值相同,就选择最左边的,然后在左端点\(l\)和找出来的\(p\)点之间二分,可以二分出来最小的值最低的点
key code
i64 n,l,r;
cin>>n>>l>>r;
i64 mn=n/l+l-1;
i64 p=l;
if(n/r+r-1<mn){
mn=n/r+r-1;
p=r;
}
i64 x=sqrt(n);
if(l<=x&&x<=r&&n/x+x-1<mn){
mn=n/x+x-1;
p=x;
}
if(l<=x&&x<=r&&n/x+x-1<mn){
mn=n/x+x-1;
p=x;
}
x++;
if(l<=x&&x<=r&&n/x+x-1<mn){
mn=n/x+x-1;
p=x;
}
i64 lo=l,hi=p;
while(lo<hi){
i64 x=lo+hi>>1;
if(n/x+x-1==mn)hi=x;
else lo=x+1;
}
cout<<lo<<endl;
H-Tokitsukaze and K-Sequence[计数]
H-Tokitsukaze and K-Sequence_2023牛客寒假算法基础集训营2 (nowcoder.com)
\(Tokitsukaze\) 有一个长度为 \(n\) 的序列 \(a\),她想把这个序列划分成 \(k\) 个非空子序列。定义序列的值为这个序列中只出现一次的数字的个数。
对于 \(k=1 \ldots n\),\(Tokitsukaze\) 想知道把序列 \(a\)划分成 \(k\) 个非空子序列后,所有子序列的值的和最大是多少。
请注意,子序列不一定是连续的。
这个题是根据划分的段数来判断的
比如说划分成3段,那么三次以下的数字便可以都计数,三次以上的数字只能计2次
因为必需有一个段里面是重叠的
所以就可以根据未超过这个段数量的和超过这个段数量的来计数
未超过这个段数量的数的次数在每一层都会计数一次,所以就可以记录他的增长轨迹
超过了这个段数量的就是在比这个段数量加一的地方都会被记录一次
减去这个记录值就可以得到应该得到的答案
in
1
3
2 2 1
out
1
3
3
key code
cin>>n;
mem0(b);
mem0(c);
up(1,n){cin>>a[o];b[a[o]]++;c[b[a[o]]]++;}
int res=0;
up(1,n){
res+=c[o];
cout<<res-c[o+1]<<endl;
}
J-Tokitsukaze and Sum of MxAb[观察]
J-Tokitsukaze and Sum of MxAb_2023牛客寒假算法基础集训营2 (nowcoder.com)
Tokitsukaze 最近对最大值与绝对值有了兴趣。
她有一个长度为 \(n\) 的序列 \(a\)。
定义
$ MxAb(i,j)=max(|a_i-a_j|,|a_i+a_j|)$
\(Tokitsukaze\) 想知道对于所有的 \(i\), \(j\) (\(1 \leq i,j \leq n\)),\(MxAb(i,j)\)的和为多少。
即求下列式子
\(\sum_{i=1}^{n} \sum_{j=1}^{n} MxAb(i,j)\)
in
2
1
1
2
1 -2
out
2
12
这个题看样例很好理解
样例1解释:
第一组测试数据:\(MxAb(1,1)=max(|1-1|,|1+1|)=2\)
第二组测试数据:
\(MxAb(1,1)=max(|1-1|,|1+1|)=2\)
\(MxAb(1,2)=max(|1-(-2)|,|1+(-2)|)=3\)
\(MxAb(2,1)=max(|(-2)-1|,|(-2)+1|)=3\)
\(MxAb(2,2)=max(|(-2)-(-2)|,|(-2)+(-2)|)=4\)
所以总和为\(2+3+3+4=12\)
显然就是每一个数的绝对值加上两遍
key code
void solve(){
//try it again.
cin>>n;
up(1,n)b[o]=0;
up(1,n){cin>>a[o];if(a[o]<0)a[o]=-a[o];}
up(1,n){b[o]=a[o]+b[o-1];}
int tt=2*n;
cout<<b[n]*tt<<endl;
}
L-Tokitsukaze and Three Integers[预处理]
L-Tokitsukaze and Three Integers_2023牛客寒假算法基础集训营2 (nowcoder.com)
Tokitsukaze 有一个长度为 \(n\) 的序列 \(a\) 和一个正整数 \(p\) 。
对于所有 \(x=0 \ldots p-1\),\(Tokitsukaze\) 想知道三元组\([i,j,k]\)的数量,满足:
• \(i \ne j\) $ and$ $ i \ne k $ $ and$ \(j \ne k\)
•$ (a_i \cdot a_j + a_k) \equiv x \text{ (mod } p)$
in
3 3
1 2 3
out
0 2 4
可以将乘积预处理出来
fup(i,0,n-1)
fup(j,0,n-1){
if(i!=j)b[a[i]*a[j]%p]++;
}
进行搭配
fup(i,0,p-1)
fup(j,0,p-1){
ans[(i+j)%p]+=b[i]*c[j];
}
容斥
fup(i,0,n-1)
fup(j,0,n-1){
if(i!=j)ans[(a[i]*a[j]+a[i])%p]-=2;
}
key code
void solve(){
//try it again.
int n,p;
cin>>n>>p;
vector<int>a(n),c(p);
fup(i,0,n-1){
cin>>a[i];
a[i]%=p;
c[a[i]]++;
}
vector<i64>ans(p),b(p);
fup(i,0,n-1)
fup(j,0,n-1){
if(i!=j)b[a[i]*a[j]%p]++;
}
fup(i,0,p-1)
fup(j,0,p-1){
ans[(i+j)%p]+=b[i]*c[j];
}
fup(i,0,n-1)
fup(j,0,n-1){
if(i!=j)ans[(a[i]*a[j]+a[i])%p]-=2;
}
fup(i,0,p-1)cout<<ans[i]<<" \n"[i == p-1];
}

浙公网安备 33010602011771号