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;
}
posted @ 2025-04-02 15:40  D06  阅读(23)  评论(0)    收藏  举报
//雪花飘落效果