主题介绍

介绍

比赛链接

Problem A. Who Can Win

题意:

有T个样例,每个样例有S个提交,每个提交由队伍名字、题目、提交时间、提交状态构成。

提交状态分为AC、RE、UN,如果RE,罚时加20min,如果AC,以提交时间为基础罚时,如果UN,可能AC或WA。

将最终可能夺得冠军的队伍按字典序输出。

题解:

按照提交时间进行排序,利用map存储队伍名字+题目、状态+时间。这样可以统计一支队伍在某道题目上的状态。

如果已经AC或UN,将跳过后面对于这道题目的所有提交。如果RE,罚时+20。

不考虑UN,只考虑AC和RE,最后得到冠军AC题目数量和罚时。

再将队伍进行字典序排序,将所有UN考虑变成AC,大于等于前面冠军的输出。

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int li=1e6;
int t,n,len;
struct sut{
	string sna;
	char ti;
	int shi;
	int zhuang;
	int unti,unshi;
}a[N],b[N];
bool cmp(sut x,sut y){
	if(x.shi!=y.shi) return x.shi<y.shi;
	if(x.zhuang!=y.zhuang){
		return x.zhuang>y.zhuang;
	}
	return x.sna<y.sna;
}
bool cmp1(sut x,sut y){
	return x.sna<y.sna;
}
map<string,int> mp1;
map<string,int> mp2;//哈希
void solve(){
	cin>>n;
	len=0;
	for(int i=1;i<=n;i++){
		b[i].shi=b[i].unshi=b[i].unti=b[i].zhuang=0;
		b[i].sna="";b[i].ti=0;
		a[i].shi=a[i].unshi=a[i].unti=a[i].zhuang=0;
		a[i].sna="";a[i].ti=0;
	}
	mp1.clear();mp2.clear();
	for(int i=1;i<=n;i++){
		cin>>a[i].sna>>a[i].ti>>a[i].shi;
		string s1;cin>>s1;
		if(s1=="Accepted") a[i].zhuang=1;
		else if(s1=="Rejected") a[i].zhuang=2;
		else a[i].zhuang=3;
	}
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++){
		int tmp1=a[i].zhuang,tmp2=a[i].shi;
		string s1=a[i].sna+a[i].ti;
		if(mp1[s1]==0){
			if(tmp1==2) mp1[s1]=tmp1*li+20;
			else mp1[s1]=tmp1*li+tmp2;
		}else{
			int tmp3=mp1[s1]/li;
			if(tmp3==1||tmp3==3) continue;
			else if(tmp3==2){
				if(tmp1==1){
					int zzz=mp1[s1];
					mp1[s1]=li+zzz%li+tmp2;
				}else if(tmp1==2) mp1[s1]+=20;
				else{
					int zzz=mp1[s1];
					mp1[s1]=3*li+zzz%li+tmp2;
				}
			}
		}
	}
	for(map<string,int>::iterator it=mp1.begin();it!=mp1.end();it++){
		string s1=(*it).first;
		int tmp1=(*it).second/li,tmp2=(*it).second%li;
		s1.erase(s1.size()-1,1);
		if(mp2[s1]==0) mp2[s1]=++len;
		b[mp2[s1]].sna=s1;
		if(tmp1==1){
			b[mp2[s1]].zhuang++;
			b[mp2[s1]].shi+=tmp2;
		}else if(tmp1==3){
			b[mp2[s1]].unti++;
			b[mp2[s1]].unshi+=tmp2;
		}
	}
	int ans1=0,ans2=1e9;
	for(int i=1;i<=len;i++){
		if(b[i].zhuang>ans1){
			ans1=b[i].zhuang;
			ans2=b[i].shi;
		}else if(b[i].zhuang==ans1){
			ans2=min(ans2,b[i].shi);
		}
	}
	sort(b+1,b+len+1,cmp1);
	for(int i=1;i<=len;i++){
		int a1=b[i].zhuang,a2=b[i].unti,a3=b[i].shi,a4=b[i].unshi;
		if((a1>ans1)||(a1==ans1&&a3<=ans2)||(a1+a2>ans1)||(a1+a2==ans1&&a3+a4<=ans2)){
			cout<<b[i].sna<<" ";
		}
	}
	cout<<"\n";
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>t;
	while(t--) solve();
	return 0;
}

Problem B. Creating Chaos

题意:

给定一个序列1~n,你删除k个数,使得剩下的数两两之间 $$gcd( a_i-a_j ,n)$$ 之和最少,输出一组方案即可。

题解:

删除1~k即可。严格证明请看B题证明

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,k;cin>>n>>k;
	for(int i=1;i<=k;i++) cout<<i<<" ";
	return 0;
}

Problem C. Canvas Painting

题意:

给定一个长度为n的数列1~n,接下来给定m个区间,可以任意顺序排列这些区间,然后依次对每个区间 \([l,r]\) 选定其中两个位置,让其中一个位置的数等于另一个位置的数,问数列不同数字的个数最终的最小值。

题解:

贪心。我们先对区间进行排序,以左端点从小到大,相同看右端点从小到大。

先排除掉长度为1的区间,不会发挥作用。

如果区间的左端点单调递增,那么我每次只需要选中最靠左的两个数,每次操作可以让颜色数-1,此时答案是n-m。

如果区间左端点相等不递增,可以先贪心用掉区间长度最短的那个(即右端点小的),把其他与左端点相等的区间左端点加一。

整个过程需要排除长度本来就为1的区间和在操作中长度变为1的区间,并动态维护顺序。可以用堆来实现。时间复杂度 \(O(n\log n)\)。

#include<bits/stdc++.h>
using namespace std;
const int N=2e6;
int n,m;
struct qu{
	int l,r;
	friend bool operator < (qu a,qu b){
		if(a.l!=b.l) return a.l<b.l;
		return a.r<b.r;
	}
}e[N];
priority_queue<int,vector<int>,greater<int> > pq;
void solve(){
	cin>>m>>n;
	for(int i=1;i<=m;i++){
		cin>>e[i].l>>e[i].r;
	}
	sort(e+1,e+m+1);
	int t=1,xl=0,k=0;//t第几区间,k删除数目
	while(t<=m){
		xl=e[t].l;
		do{
			while(t<=m&&e[t].l==xl){//将左端点相同的放入队列
				if(xl<e[t].r) pq.push(e[t].r);
				t++;
			}
			if(pq.empty()) break;//防止上一步没有导致下一步pop错误
			xl++,k++,pq.pop();//使用top这个区间
			while(!pq.empty()&&xl>=pq.top()) pq.pop();//留下所有右区间大于xl的
		}while(!pq.empty());
	}
	cout<<n-k<<"\n";
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int T;cin>>T;
	while(T--) solve();
	return 0;
}

Problem D. Min-Max Tree

树形DP,不会。ICPC2025 网络赛(一)简单习题详解(7/13)

Problem G. Sorting

题意:

给定m对二元组,如果左下标对应数字大于右下标对应数字,你就可以使用这些二元组交换两者为下标的位置。能否将所有排列经过任意次操作后变成从小到大的有序。

题解:

观察发现如果出现所有的 \([i,i+1]\),即可将所有排列变为有序。

个人推论。如果取消掉数字大于才能排序的规则,则联通即可。

#include<bits/stdc++.h>
using namespace std;
int n,m,a[200010];
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>m;
	while(m--){
		int l,r;cin>>l>>r;
		if(l+1==r) a[l]=1;
	}
	for(int i=1;i<n;i++)
		if(a[i]!=1){
			cout<<"No\n";return 0;
		}
	cout<<"Yes\n";
	return 0;
}

Problem I. Knapsack Problem

题意:

给一个具有n个顶点和m条边的无向图,每条边具有一个权值w,固定背包容量V。一开始带一个背包,经过相应权值的边就把权值放入背包,如果放不下就新开一个背包并且放进新背包。

给定节点T,问每个节点到T分别所需要消耗的最小背包数。

题解:

Dijkstra板子,维护每个点的(最小背包数,所用容量)即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=0x3f3f3f3f;
const int N=1e5+10,M=5e5+10;
int n,m,T;
ll V;
struct num{
	ll v,c;//背包个数,背包所装容量
	num(ll vv=0,ll cc=0){v=vv,c=cc;}
	friend bool operator < (num a,num b){
		if(a.v==b.v) return a.c<b.c;
		return a.v<b.v;
	}
	num operator + (int w)const{
		if(c+w<=V) return num(v,c+w);
		return num(v+1,w);
	}
};
num dis[N];
struct edge{
	int v;//终点
	num w;
	bool operator <(edge b)const{
		return b.w<w;
	}
};
struct eg{
	ll v,w;
};
priority_queue<edge> pq;
vector<eg> E[N];
void add(int u,int v,ll w){
	E[u].push_back({v,w});
}
int main(){
	cin>>n>>m>>V>>T;
	for(int i=1;i<=n;i++) dis[i]=num(n+1,0);//等同初始最大
	dis[T]=num(0,0);//起点
	pq.push({T,dis[T]});//起点放入优先队列
	while(m--){//存边
		ll u,v,w;cin>>u>>v>>w;add(v,u,w);add(u,v,w);
	}
	while(!pq.empty()){
		int u=pq.top().v;pq.pop();
		for(int i=0;i<E[u].size();i++){
			eg z=E[u][i];
			if(dis[u]+z.w<dis[z.v])
				pq.push({z.v,dis[z.v]=dis[u]+z.w});
		}
		while(!pq.empty()&&dis[pq.top().v]<pq.top().w) pq.pop();
	}
	for(int i=1;i<=n;i++){
		if(dis[i].v<=n) cout<<dis[i].v+1<<" ";
		else cout<<"-1 ";
	}
}

Problem M. Teleporter

题意:

有n个城市,每个城市有n-1条道路连接,每条道路有w的权值花费。每个城市互相到达,形成一棵树。

有m条传送路径,连接某两座城市,无需任何代价即可到达。

想知道使用传送阵不超过k次,从1到各个城市的最小花费之和。

数据范围:1<=n<=5000,0<=m<=10000,1<=w<=10,0<=k<=n

题解:

分层图做这道题目超时是因为每一层中有很多重复的路径。

把每一层分开算,计算一下在走了 i-1 条附加边的基础上再走一条会给哪几个点的dist缩小。然后用这几个点跑最短路即可。

时间复杂度是 \(O(n^2\log n+nm)\) 但是跑不满,还有 \(O(n(n+m))\) 。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf=1e18;
int n,m;
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>m;
	vector<vector<pair<int,int> > > to(n+1);//存图
	for(int i=1;i<n;i++){
		int u,v,w;cin>>u>>v>>w;
		to[u].push_back({v,w});
		to[v].push_back({u,w});
	}
	vector<pair<int,int> > xl(m);//传送
	for(int i=0;i<m;i++) cin>>xl[i].first>>xl[i].second;
	vector<ll> dis(n+1,inf);
	priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<> > q;
	q.push({0,1});
	dis[0]=dis[1]=0;
	vector<int> in(n+1);
	auto dj=[&]{
		in.assign(n+1,0);
		while(!q.empty()){
			auto [t,now]=q.top();q.pop();
			if(in[now]) continue;
			in[now]=true;
			for(auto &[e,d1]:to[now]){
				if(d1+t<dis[e]){
					dis[e]=d1+t;
					q.push({dis[e],e});
				}
			}
		}
		return accumulate(dis.begin(),dis.end(),0ll);
	};
	cout<<dj()<<"\n";
	for(int k=1;k<=n;k++){
		vector<ll> nw(dis);
		for(auto &[a,b]:xl){
			nw[a]=min(nw[a],dis[b]);
			nw[b]=min(nw[b],dis[a]);
		}
		for(int i=1;i<=n;i++){
			if(nw[i]<dis[i]) q.push({nw[i],i}),dis[i]=nw[i];
		}
		cout<<dj()<<"\n";
	}
	return 0;
}