2025春季杭电多校3题解

翻车了

1005

没什么好说的,并查集维护就行

void solve(){
	int n;
	cin>>n;

	map<int,bool>vis;
	vector<int>a(n+1);
	for(int i=1;i<=n;i++){
		cin>>a[i];
		vis[i]=true;
	}

	vector<int>fa(n+1);
	iota(fa.begin(),fa.end(),0);

	auto find=[&](int x)->int{
		while(x!=fa[x]){
			x=fa[x]=fa[fa[x]];
		}
		return x;
	};

	auto merge=[&](int x,int y)->void{
		x=find(x),y=find(y);
		if(x!=y){
			fa[x]=y;
		}
	};

	for(int i=1;i<=n;i++){
		int x=i-a[i],y=i+a[i];
		if(vis[x])
			merge(x,i);
		if(vis[y])
			merge(y,i);
	}

	vector<int>ans;
	for(int i=1;i<=n;i++){
		if(find(i)==i){
			ans.push_back(i);
		}
	}

	cout<<ans.size()-1<<endl;
}

1010

  假设配送站的位置为\((a,b)\),那么以\((a,b)\)为原点作坐标系,客户的位置\((x,y)\)在每个象限的情况都考虑下,推出每种情况曼哈顿距离的表达式(去掉绝对值)。发现只用维护全局\(max(x+y),max(x-y),max(y-x),min(x+y)\)。这个维护输入的时候就能预处理出来。然后枚举所有的配送站位置,对每一个配送站位置计算四个象限的曼哈顿距离,求出最大值,再用最大值更新最后的答案(要求最大值最小,就是取所有最大值中的最小值)。详细见代码:

void solve(){
	int n,m;
	cin>>n>>m;

	int s1=-INF,s2=INF,s3=-INF,s4=-INF;
	for(int i=1;i<=n;i++){
		int x,y;
		cin>>x>>y;

		s1=max(s1,x+y);
		s2=min(s2,x+y);
		s3=max(s3,x-y);
		s4=max(s4,y-x);
	}	

	vector<int>a(m+1),b(m+1);
	for(int i=1;i<=m;i++){
		cin>>a[i]>>b[i];
	}

	int ans=INF;

	for(int i=1;i<=m;i++){
		int r1=s1-(a[i]+b[i]);
		int r2=a[i]+b[i]-s2;
		int r3=b[i]-a[i]+s3;
		int r4=a[i]-b[i]+s4;

		int tep=max({r1,r2,r3,r4});

		ans=min(ans,tep);
	}

	cout<<ans<<endl;
}

1009

  这个题是atcode中的原,abc395的D题。我们考虑维护三个信息\(a[i]\)表示人所在的位置,\(b[i]\)表示部落所在的位置,\(c[i]\)表示位置\(i\)上的部落的编号。对于操作2,相当于把人x所在的位置改成部落y的位置;对于操作3,相当于交换壳子,即我们不交换部落中具体的人。假设部落1所在的位置为\(ip1\),部落2所在的位置为\(ip2\),那么就有\(c[ip1]\)=部落1,\(c[ip2]\)=部落2,我们交换部落这个壳子,让\(c[ip1]\)=部落2,\(c[ip2]\)=部落1,那么原本在\(ip1\)位置上的所有人所在的部落就改成了部落2,实现了交换;对于操作4,查询人在的部落,就是查询人在的位置上的部落编号。
  以上思路是\(abc395\)的解法,这道题中多了一个操作1:a 部落与 b部落结盟。结盟操作使部落合并,新部落的编号是a。两个部落结盟,那么两个部落肯定共享一个位置,直接用并查集维护,实现共享位置;新部落的编号是a,那我就找部落所在位置的根节点,然后把这个位置上的部落编号改成a就行了。查询的时候查人在的位置上的部落编号,人的位置须是根节点的位置。有点乱了,详细见代码。

struct DSU{
	vector<int>fa,siz;
	DSU(int _n):fa(_n+1,0),siz(_n+1,1){
		iota(fa.begin(),fa.end(),0);
	}	
	int find(int x){
		while(x!=fa[x])
			x=fa[x]=fa[fa[x]];
		return x;
	}
	void merge(int x,int y){
		x=find(x),y=find(y);
		if(x!=y){
			fa[x]=y;
			siz[y]+=siz[x];
		}
	}
};
void solve(){
	int n,q;
	cin>>n>>q;

	vector<int>a(n+1);//维护人所在的位置
	vector<int>b(n+1);//维护部落所在的位置
	vector<int>c(n+1);//维护位置上的部落编号

	for(int i=1;i<=n;i++){
		a[i]=b[i]=c[i]=i;
	}

	DSU dsu(n+1);

	while(q--){
		int opt;
		cin>>opt;

		if(opt==1){
			int x,y;
			cin>>x>>y;
			dsu.merge(b[x],b[y]);
			int r=dsu.find(b[x]);
			c[r]=x;
			b[x]=b[y]=r;
		}else if(opt==2){
			int x,y;
			cin>>x>>y;
			a[x]=b[y];
		}else if(opt==3){
			int x,y;
			cin>>x>>y;
			int ip1=b[x],ip2=b[y];
			c[ip1]=y,c[ip2]=x;
			swap(b[x],b[y]);
		}else{
			int x;
			cin>>x;
			cout<<c[dsu.find(a[x])]<<endl;
		}
	}

}

1003

  我要补一下优先队列了,不然迟早被创死。
  初始时,针对每个能力维度建立小根堆,按该维度的要求值升序排列所有公司,确保优先处理最低门槛的面试机会。每次循环遍历所有维度,从堆顶依次取出当前能力可满足的公司,并累计其已通过的维度数;当某公司所有维度均达标时,立即将其加入处理队列,同步提升各维度能力值,触发新一轮的解锁检查——此时其他公司可能因能力提升而满足新的维度要求。此过程通过多轮迭代不断“解锁”可达标的公司,直至某轮循环中无新增公司可处理为止,最终通过已处理公司总数是否等于总数量判断可行性。该策略通过贪心选择最小要求的公司优先处理,并实时更新能力状态,确保了最优的解锁顺序和全局正确性。

using T2=tuple<int,int>;
using pq=priority_queue<T2,vector<T2>,greater<>>;

void solve(){
	int n,m;
	cin>>n>>m;

	vector<int>a(m+1);
	for(int i=1;i<=m;i++){
		cin>>a[i];
	}

	vector c(n+1,vector<int>(m+1));
	vector w(n+1,vector<int>(m+1));

	vector<pq>q(m+1);

	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>c[i][j];
			q[j].emplace(c[i][j],i);
		}
		for(int j=1;j<=m;j++){
			cin>>w[i][j];
		}
	}

	vector<int>cnt(n+1,0);
	int ans=0;

	while(true){
		bool flag=false;
		for(int j=1;j<=m;j++){
			while(!q[j].empty()){
				auto[x,y]=q[j].top();
				if(x>a[j]){
					break;
				}
				flag=true;
				q[j].pop();
				cnt[y]++;
				if(cnt[y]==m){
					for(int k=1;k<=m;k++){
						a[k]+=w[y][k];
					}
					ans++;
				}
			}
		}
		if(!flag) break;
	}

	if(ans==n){
		cout<<"YES"<<endl;
	}else{
		cout<<"NO"<<endl;
	}
}

1007

  可持久化trie的纯板子题,那个公式等价于\(a_{i}异或x\)。然后可持久化就行。学可持久化trie的第一道例题就是这个QwQ

const int N = 2e5+10;

int trie[N * 33][2], rt[N], id[N*33], idx;

void insert(int num, int k) {
	rt[k] = ++idx;
	id[idx] = k;
	int x = rt[k - 1], y = rt[k];
	for (int i = 31; i >= 0; i--) {
		int j = num >> i & 1;

		trie[y][!j] = trie[x][!j];
		trie[y][j] = ++idx;
		id[idx] = k;
		x = trie[x][j], y = trie[y][j];
	}
}
int query(int l, int r, int num) {
	int p = rt[r], ans = 0;
	for (int i = 31; i >= 0; i--) {
		int j = num >> i & 1;
		if (trie[p][!j]) {
			if (l <= id[trie[p][!j]] && id[trie[p][!j]] <= r) {
				ans += 1 << i;
				p = trie[p][!j];
			} else {
				p = trie[p][j];
			}
		} else {
			p = trie[p][j];
		}
	}
	return ans;
}

void solve() {
	int n, q;
	cin >> n >> q;
	for (int i = 1; i <= n; i++) {
		int x;
		cin >> x;
		insert(x, i);
	}
	while (q--) {
		int l, r, num;
		cin >> l >> r >> num;
		cout << query(l, r, num) << endl;
	}
	memset(rt, 0, sizeof(rt));
	memset(trie, 0, sizeof(trie));
	idx = 0;
}
posted @ 2025-09-11 18:01  CyansQwQ  阅读(21)  评论(0)    收藏  举报