2025“钉耙编程”中国大学生算法设计春季联赛(3)
修复公路
#include <bits/stdc++.h>
using namespace std;
int a[300005],fa[300005];
int get(int x)
{
if(fa[x]==x)
{
return x;
}
return fa[x]=get(fa[x]);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
iota(fa+1,fa+n+1,1);
int ans=n-1;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(i-a[i]>=1)
{
if(get(i-a[i])!=get(i))
{
ans--;
fa[get(i-a[i])]=get(i);
}
}
if(i+a[i]<=n)
{
if(get(i+a[i])!=get(i))
{
ans--;
fa[get(i+a[i])]=get(i);
}
}
}
cout<<ans<<endl;
}
return 0;
}
选择配送
- 问题呈现的是曼哈顿距离(三个字)、切比雪夫距离(四个字);除以 1、除以 2
- std::max可以返回初始化器列表 ilist 中值的最大者,例如:
max({abs(a-x[1]),abs(a-x[n]),abs(b-y[1]),abs(b-y[n])})
#include <bits/stdc++.h>
using namespace std;
int x[1000005],y[1000005];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--)
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int u,v;
cin>>u>>v;
x[i]=u+v;
y[i]=u-v;
}
sort(x+1,x+n+1);
sort(y+1,y+n+1);
int ans=INT_MAX;
for(int i=1;i<=m;i++)
{
int u,v,a,b;
cin>>u>>v;
a=u+v;
b=u-v;
ans=min(ans,max(max(abs(a-x[1]),abs(a-x[n])),max(abs(b-y[1]),abs(b-y[n]))));
}
cout<<ans<<endl;
}
return 0;
}
数列计数
- 对 Lucas 定理有了更加深刻的理解:若 p 是质数, 就可以把 n 和 m 表示成 p 进制数 ,对 p 进制下的每⼀位分别计算组合数,最后再乘起来
#include <bits/stdc++.h>
using namespace std;
const int mod=998244353;
int c[1005][1005];
int a[100005],l[100005],f[35][2];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
/*
c[0][0]=c[1][0]=c[1][1]=1;
for(int i=2;i<=25;i++)
{
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++)
{
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
cout<<i<<':';
for(int j=0;j<=i;j++)
{
cout<<c[i][j]%2<<" ";
}
cout<<endl;
}
*/
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
long long ans=1;
for(int i=1;i<=n;i++)
{
cin>>l[i];
l[i]=min(l[i],a[i]);
f[30][0]=0;
f[30][1]=1;
for(int j=29;j>=0;j--)
{
if(((a[i]>>j)&1)==0)
{
if((l[i]>>j)&1)
{
f[j][0]=f[j+1][0]+f[j+1][1];
f[j][1]=0;
}
else
{
f[j][0]=f[j+1][0];
f[j][1]=f[j+1][1];
}
}
else
{
if((l[i]>>j)&1)
{
f[j][0]=f[j+1][0]*2+f[j+1][1];
}
else
{
f[j][0]=f[j+1][0]*2;
}
f[j][1]=f[j+1][1];
}
}
ans=ans*(f[0][0]+f[0][1])%mod;
}
cout<<ans<<endl;
}
return 0;
}
拼尽全力
- deque 典型地拥有较大的最小内存开销,开\(10^6\)个 deque 几乎必定会 MLE 。另外,stack 和 queue 都基于 deque 实现
如果你是单调队列之类的东西,建议就是用 vector 加记一个起点
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int,int> pii;
vector<pii>p[1000005];
vector<int>w[1000005];
queue<int>upd;
int a[1000005],cnt[1000005],sum,q[1000005];
int n,m;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=0;i<m;i++)
{
int x;
cin>>x;
w[n].push_back(x);
p[i].clear();
a[i]=0;
}
for(int i=0;i<n;i++)
{
cnt[i]=0;
for(int j=0;j<m;j++)
{
int c;
cin>>c;
p[j].push_back({c,i});
}
for(int j=0;j<m;j++)
{
int x;
cin>>x;
w[i].push_back(x);
}
}
for(int i=0;i<m;i++)
{
q[i]=0;
sort(p[i].begin(),p[i].end());
}
sum=0;
upd.push(n);
while(upd.size())
{
for(int i=0;i<m;i++)
{
a[i]+=w[upd.front()][i];
while(q[i]<p[i].size()&&a[i]>=p[i][q[i]].first)
{
cnt[p[i][q[i]].second]++;
if(cnt[p[i][q[i]].second]==m)
{
sum++;
upd.push(p[i][q[i]].second);
}
q[i]++;
}
}
upd.pop();
}
sum==n? cout<<"YES\n":cout<<"NO\n";
for(int i=0;i<=n;i++)
{
w[i].clear();
}
}
return 0;
}
部落冲突
#include <bits/stdc++.h>
using namespace std;
int fa[2000005],loc[1000005],tri[1000005];
int get(int x)
{
if(fa[x]==x)
{
return x;
}
return fa[x]=get(fa[x]);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--)
{
int n,q;
cin>>n>>q;
iota(fa+1,fa+n+1,1);
iota(loc+1,loc+n+1,1);
iota(tri+1,tri+n+1,1);
for(int i=1;i<=n;i++)
{
fa[i+n]=i;
}
for(int i=1;i<=q;i++)
{
int opt,a,b;
cin>>opt>>a;
if(opt==1)
{
cin>>b;
fa[loc[b]]=loc[a];
}
else if(opt==2)
{
cin>>b;
fa[a+n]=loc[b];
}
else if(opt==3)
{
cin>>b;
swap(loc[a],loc[b]);
tri[loc[a]]=a;
tri[loc[b]]=b;
}
else
{
cout<<tri[get(a+n)]<<"\n";
}
}
}
return 0;
}
宝石商店
- 复习了可持久化01-Trie
#include <bits/stdc++.h>
using namespace std;
int a[200005];
int e[10000005],t[10000005][2],root[200005];
int tot,cnt;
void insert(int x,int k)
{
int p=root[cnt],q=root[++cnt]=++tot;
for(int i=30;i>=0;i--)
{
t[q][0]=t[p][0];
t[q][1]=t[p][1];
t[q][(x>>i)&1]=++tot;
p=t[p][(x>>i)&1];
q=t[q][(x>>i)&1];
e[tot]=k;
}
}
int ask(int l,int cur,int x)
{
int ans=0;
for(int i=30;i>=0;i--)
{
if(l<=e[t[cur][((x>>i)&1)^1]])
{
cur=t[cur][((x>>i)&1)^1];
ans+=(1<<i);
}
else
{
cur=t[cur][(x>>i)&1];
}
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--)
{
int n,Q;
cin>>n>>Q;
cnt=tot=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
insert(a[i],i);
}
for(int i=1;i<=Q;i++)
{
int l,r,x;
cin>>l>>r>>x;
cout<<ask(l,root[r],x)<<"\n";
}
}
return 0;
}
弯曲筷子
- 排序以后做一个很简单的DP就好了,不明白赛时通过率为什么那么低
#include <bits/stdc++.h>
using namespace std;
#define int long long
int g[300005][2];
struct t1
{
int c;
bool f;
}t[300005];
bool cmp(t1 a,t1 b)
{
return a.c<b.c;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--)
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>t[i].c;
t[i].f=false;
}
for(int i=1;i<=m;i++)
{
int x;
cin>>x;
t[x].f=true;
}
sort(t+1,t+n+1,cmp);
int la=1;
for(int i=1;i<=n;i++)
{
if(t[i].f==false)
{
g[i][0]=min(g[i-1][0],g[la-1][0]+(t[i].c-t[la].c)*(t[i].c-t[la].c));
}
else
{
g[i][0]=(1ll<<60);
for(int j=i-1;j>=la;j--)
{
g[i][0]=min(g[i][0],g[j-1][0]+(t[i].c-t[j].c)*(t[i].c-t[j].c));
}
la=i;
}
}
cout<<g[n][0]<<endl;
}
return 0;
}
序列期望
- 发现统计合法的区间数量比较难做,正难则反,尝试统计非法的区间数量,式子更简单之后做法也更清晰了
- 问题的核心难点在于la和l的大小关系未知,为此应从值域的角度考虑。离线扫描右端点(值域有限,比直接sort更加简洁的做法是用桶排序),对 la 实时建立值域树状数组,通过查询L自动分类两种情况
#include <bits/stdc++.h>
#define int long long
using namespace std;
const signed mod=1000000007;
int power(int n,int p)
{
if(p==0)
{
return 1;
}
long long tmp=power(n,p/2);
if(p%2==1)
{
return tmp*tmp%mod*n%mod;
}
return tmp*tmp%mod;
}
int c[4][200005],n;
int lowbit(int n)
{
return n&(-n);
}
void add(int n1,int va,int op)
{
while(n1<=n)
{
c[op][n1]+=va;
n1+=lowbit(n1);
}
}
int ask(int n1,int op)
{
int ans=0;
while(n1>0)
{
ans+=c[op][n1];
n1-=lowbit(n1);
}
return ans%mod;
}
typedef pair<int,int> pii;
vector<pii>a[200005];
int h[200005],la[200005],ans[200005],tot[200005];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--)
{
int q;
cin>>n>>q;
tot[1]=1;
for(int i=2;i<=n;i++)
{
tot[i]=(tot[i-1]+(1+i)*i/2)%mod;
}
for(int i=1;i<=n;i++)
{
a[i].clear();
int x;
cin>>x;
if(h[x])
{
la[i]=h[x];
}
h[x]=i;
}
for(int i=1;i<=q;i++)
{
int l,r;
cin>>l>>r;
a[r].emplace_back(l,i);
}
for(int i=1;i<=n;i++)
{
if(la[i])
{
add(la[i],1,0);
add(la[i],i,1);
add(la[i],la[i],2);
add(la[i],i*la[i],3);
}
for(auto [l,id]:a[i])
{
ans[id]=tot[i-l+1];
int sum1=ask(i,0)-ask(l-1,0);
int sumi=ask(i,1)-ask(l-1,1);
int sumla=ask(i,2)-ask(l-1,2);
int sumlai=ask(i,3)-ask(l-1,3);
ans[id]-=(sum1*(1+i-l-l*i)+sumi*(l-1)+sumla*(i+1)-sumlai);
ans[id]%=mod;
(ans[id]*=power((i-l+1)*(i-l+2)/2%mod,mod-2))%=mod;
}
}
for(int i=1;i<=q;i++)
{
cout<<(ans[i]+mod)%mod<<"\n";
}
memset(h,0,sizeof(h[0])*(n+1));
memset(la,0,sizeof(la[0])*(n+1));
for(int i=0;i<4;i++)
{
memset(c[i],0,sizeof(c[i][0])*(n+1));
}
}
return 0;
}
宾果游戏
#include <bits/stdc++.h>
#define int long long
using namespace std;
const signed mod=998244353;
bool h[1000005],l[1000005];
int sum[1000005];
int power(int n,int p)
{
if(p==0)
{
return 1;
}
long long tmp=power(n,p/2);
if(p%2==1)
{
return tmp*tmp%mod*n%mod;
}
return tmp*tmp%mod;
}
long long jc[1000005],jcinv[1000005];
void pre()
{
jc[0]=1;
for(int i=1;i<=1000000;i++)
{
jc[i]=jc[i-1]*i%mod;
}
jcinv[1000000]=power(jc[1000000],998244351);
for(int i=1000000-1;i>=0;i--)
{
jcinv[i]=jcinv[i+1]*(i+1)%mod;
}
}
long long c(int n,int m)
{
if(m>n)
{
return 0;
}
return jc[n]*jcinv[m]%mod*jcinv[n-m]%mod;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
pre();
for(int i=1;i<=1000000;i++)
{
sum[i]=(sum[i-1]+power(i,mod-2)%mod)%mod;
}
int T;
cin>>T;
while(T--)
{
int n,m,k;
cin>>n>>m>>k;
memset(h,false,sizeof(h));
memset(l,false,sizeof(l));
for(int i=1;i<=k;i++)
{
int x,y;
cin>>x>>y;
h[x]=l[y]=true;
}
int p=n-accumulate(h+1,h+n+1,0);
int q=m-accumulate(l+1,l+m+1,0);
if(p==0&&q==0)
{
cout<<-1<<endl;
continue;
}
int ans=0;
for(int i=0;i<=p;i++)
{
for(int j=0;j<=q;j++)
{
if(i==0&&j==0)
{
continue;
}
int cnt=i*m+j*n-i*j;
if((i+j)%2)
{
ans=(ans+c(p,i)*c(q,j)%mod*sum[cnt]%mod*n%mod*m%mod)%mod;
}
else
{
ans=(ans-c(p,i)*c(q,j)%mod*sum[cnt]%mod*n%mod*m%mod)%mod;
}
}
}
cout<<(ans+mod)%mod<<endl;
}
return 0;
}

浙公网安备 33010602011771号