2026牛客寒假算法基础集训营6
K. 小L的游戏1
做法有很多,可以去二分一下两人总共至少需要操作多少次,使得总和大于等于 \(z\)
判断最后的次数的奇偶性即可
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long;
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;
void solve(){
int n,m,z;
int x=0;
cin>>n>>m>>z;
int l=1,r=(z+m+n-1)/(n+m)*2+100;
auto check=[&](int x)-> bool {
int sum=0;
sum+=(x+1)/2*n;
sum+=(x)/2*m;
return sum>=z;
};
while(l<r){
int mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid+1;
}
if(l&1) cout<<0;
else cout<<1;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--) solve();
return 0;
}
H. 小L的数组
注意值域,不管怎么操作,最多会有 2048 个结果。所以对每一个位置,维护走到当前位置会有多少个结果即可
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long;
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;
void solve(){
int n;
cin>>n;
vector<int> a(n+1),b(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
}
vector<int> st(2048);
st[0]=1;
for(int i=1;i<=n;i++){
vector<int> t(2048);
for(int x=0;x<2048;x++){
if(st[x]!=1) continue;
t[max(0ll,x-a[i])]=1;
t[x^b[i]]=1;
}
st=t;
}
for(int i=2047;i>=0;i--){
if(st[i]){
cout<<i;
return;
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
// cin>>ct;
while(ct--) solve();
return 0;
}
G. 小L的散步
处理出所有缝隙的位置,再处理出所有脚步的位置(脚步位置由脚后跟和脚尖两个位置决定)
对每个脚步的位置,用 upper_bound 去判断一下,有没有一个缝隙恰好在这个脚步内部即可
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long;
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;
void solve(){
int n,m,l;
cin>>n>>m>>l;
vector<int> x(n+1),y(m+1);
for(int i=1;i<=n;i++){
cin>>x[i];
x[i]+=x[i-1];
}
for(int i=1;i<=m;i++){
cin>>y[i];
y[i]+=y[i-1];
}
for(int i=1;i<=m;i++){
int a=y[i-1],b=y[i];
auto it=upper_bound(x.begin(),x.end(),a);
if(it!=x.end()){
//后脚的起点位置为a,终点为a+l
//前脚:[b,b+l]
auto val=*it;
if((val>a && val<a+l)){
cout<<"YES";
return;
}
}
it=upper_bound(x.begin(),x.end(),b);
if(it!=x.end()){
//后脚的起点位置为a,终点为a+l
//前脚:[b,b+l]
auto val=*it;
if((val>b && val<b+l)){
cout<<"YES";
return;
}
}
}
cout<<"NO";
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
// cin>>ct;
while(ct--) solve();
return 0;
}
A. 小L的三角尺
关键点:
- 每次打磨的长度一定要是整数
- 最多打磨 \(1e6\) 次
所以可以每次只打磨 \(t=1\)
而对于所有的尺子,计算打磨掉 \(t=1\) 会减小多少长度,放入优先队列维护即可
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long;
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;
struct node{
double x,y;
double val;
bool operator<(const node &t) const{
return val<t.val;
}
};
double cal(double a,double b){
return sqrt(a*a+b*b);
}
void solve(){
int n,w;
cin>>n>>w;
double sum=0;
priority_queue<node> q;
for(int i=1;i<=n;i++){
double a,b;
cin>>a>>b;
sum+=cal(a,b);
q.push({a,b,cal(a,b)-cal(a,b-1)});
}
double ans=0;
for(int i=1;i<=w;i++){
if(q.size()==0) continue;
auto [a,b,val]=q.top();
q.pop();
ans+=val;
if(b>1){
q.push({a,b-1,cal(a,b-1)-cal(a,b-2)});
}
}
printf("%.8lf",sum-ans);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
// cin>>ct;
while(ct--) solve();
return 0;
}
B. 小L的彩球
两步,第一步是转化问题模型,第二步是组合数学
将左边的盒子看成 \(1\),右边的盒子看成 \(0\)
问题可以转化为:一个长度为 \(n\) 的字符串,由 \(x\) 个 \(1\) 和 \(n-x\) 个 \(0\) 组成,且字符串中必须要有 \(t\) 个 01 或 10
将连续的 \(1\) 和 \(0\) 压缩成一个,假设第一个位置是 \(0\),则:010101010... (长度为 \(t\))
则问题转化为:
将 \(x\) 个 \(0\) 填入 \(\lceil t/2 \rceil\) 个位置,且每个位置最少填 \(1\) 个的填法
将 \(n-x\) 个 \(1\) 填入 \(\lfloor t/2 \rfloor\) 个位置,且每个位置最少填 \(1\) 个的填法
此时,就是组合数学的隔板法。oiwiki 可搜
对于第一个位置是 \(1\) 的情况,同理。
两种情况结果相加即可
注意需要特判 \(t=0\) 的情况
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long;
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 998244353;
const int N=1e6;
vector<int> fact(N+2),infact(N+2);
int qmi(int a,int b,int p){
int res=1;
while(b){
if(b&1) res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
void cal(){
fact[0]=1,fact[1]=1;
infact[0]=1,infact[1]=1;
for(int i=2;i<=N;i++){
fact[i]=i*fact[i-1]%mod;
}
infact[N]=qmi(fact[N],mod-2,mod);
for(int i=N-1;i>=1;i--){
infact[i]=infact[i+1]*(i+1)%mod;
}
}
int C(int a,int b){
if(a<0 || b<0 || a<b) return 0;
return fact[a]*infact[b]%mod*infact[a-b]%mod;
}
void solve(){
int n,x,t;
cin>>n>>x>>t;
if(t==0){
//因为分成 0 段不符合隔板法都要求,所以不成立
if(x==n) cout<<1<<endl;
else cout<<0<<endl;
return;
}
t++;
int ans=C(x-1,(t+1)/2-1)*C(n-x-1,t/2-1)%mod;
ans+=C(x-1,(t)/2-1)*C(n-x-1,(t+1)/2-1)%mod;
cout<<ans%mod<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cal();
int ct=1;
cin>>ct;
while(ct--) solve();
return 0;
}
D. 小L的扩展
本质还是 \(BFS\) 最短路,但是有一些点有时间限制。
所以可以把队列改成优先队列,这样可以保证,使用有限制的点时,已经到达被限制的时间。
具体看代码实现
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long;
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;
void solve(){
int n,m,a,b;
cin>>n>>m>>a>>b;
vector g(n+1,vector<int>(m+1));
priority_queue<array<int,3>,vector<array<int,3>>,greater<array<int,3>>> q;
for(int i=1;i<=a;i++){
int x,y;
cin>>x>>y;
g[x][y]=-1;
q.push({0,x,y});
}
vector lim(n+1,vector<int>(m+1));
for(int i=1;i<=b;i++){
int x,y,t;
cin>>x>>y>>t;
lim[x][y]=t;
}
int dx[]={-1,0,1,0};
int dy[]={0,1,0,-1};
int ans=0;
while(q.size()){
auto [time,x,y]=q.top();
q.pop();
for(int i=0;i<4;i++){
int tx=dx[i]+x;
int ty=dy[i]+y;
if(tx<1 || tx>n || ty<1 || ty>m) continue;
if(g[tx][ty]==-1) continue;
int t=max(lim[tx][ty],time+1);
g[tx][ty]=-1;
q.push({t,tx,ty});
ans=max(ans,t);
}
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
// cin>>ct;
while(ct--) solve();
return 0;
}
E. 小L的空投
实现是简单的,主要是能不能想到思路。
如果正向考虑删除点和边,非常复杂。但是反向考虑,在图中增加边和节点则非常容易维护,使用并查集即可。
注意 unite 的过程中动态维护答案
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
using ll=long long;
using pii=pair<int,int>;
const ll inf = 1e18;
const int mod = 1e9+7;
int n,m,x,d;
class DSU{
public:
vector<int> fa,sz;
int setCount;
int n;
int ans;
DSU(){}
DSU(int n){
init(n);
}
void init(int n){
fa.resize(0);
fa.resize(n+1);
sz.resize(0);
sz.resize(n+1,1);
this->n=n;
setCount=n;
iota(fa.begin(), fa.end(), 0);
ans=0;
}
int find(int x) {
if(fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void unite(int x, int y) {
x = find(x),y = find(y);
if(x == y) return;
int f1=(sz[x]>=d),f2=(sz[y]>=d);
int f3=(sz[x]+sz[y]>=d);
ans+=(f3-f1-f2);
if(sz[x] <= sz[y] ) swap(x, y);
fa[y] = fa[x];
sz[x] += sz[y];
--setCount;
}
};
void solve(){
cin>>n>>m>>x>>d;
vector<pii> city(n+1);
vector<int> mp(n+1);
for(int i=1;i<=n;i++){
cin>>city[i].first;
mp[i]=city[i].first;
city[i].second=i;
}
sort(city.begin()+1,city.end());
reverse(city.begin()+1,city.end());
vector<vector<int>> g(n+1);
while(m--){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int> h(x+1);
for(int i=1;i<=x;i++){
cin>>h[i];
}
vector<int> ans(x+1);
DSU dsu(n);
int now=1;
for(int i=x;i>=1;i--){
while(now<=n && city[now].first>h[i]){
int u=city[now].second;
if(d==1) dsu.ans++;
for(auto v:g[u]){
if(mp[v]<=h[i]) continue;
dsu.unite(u,v);
}
now++;
}
ans[i]=dsu.ans;
}
for(int i=1;i<=x;i++){
cout<<ans[i]<<endl;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
// cin>>ct;
while(ct--) solve();
return 0;
}

浙公网安备 33010602011771号