2020-2021 ICPC Southwestern European Regional Contest (SWERC 2020)题解

A. Gratitude

题目链接

题目大意:
统计不同字符串出现的次数,按字符串出现次数由多到少依次输出,若出现次数相同,则输出出现位置靠后的字符串。

简单字符串统计题,注意次数相同时的输出即可。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const ll mod=1000000007;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
inline ll lowbit(ll x){return x&(-x);}
struct node{
	string s;
	int id;
	int num;
}p[maxn]; 
bool cmp(node a,node b){
	if(a.num!=b.num) return a.num>b.num;
	return a.id>b.id;
}
map<string,int>mp;
int main(){
	int n,k;
	cin>>n>>k;
	int cnt=1;
	getchar();//吸收空行
	for(int i=0;i<3*n;i++){
		string s;
		getline(cin,s);
		if(mp[s]==0) mp[s]=cnt++;
		int id=mp[s];
		p[id].num++;
		p[id].id=i;//易错点
		p[id].s=s;		
	}
	sort(p+1,p+cnt+1,cmp);
	for(int i=1;i<=min(k,cnt-1);i++){//注意答案的输出个数
		cout<<p[i].s<<endl;
	}
}

C. Safe Distance

题目链接

题目大意:
求:在一长为X宽为Y的区域中,从(0,0)走到(X,Y)的过程中,与其他n个人距离最小值的最大值。

求解最小值的最大值、最大值的最小值问题,一般要用到二分法搜答案
那么这道题如何对“答案”进行判断呢?
考虑有一个半径相同的圆,从(0,0)走到(X,Y),如果这个圆能够顺利通过,即不会出现圆的边界将该区域的上边界和下边界、左边界和右边界、上边界和右边界、下边界和左边界连通等情况,则说明该“答案”可行,放大范围,继续搜答案;否则,说明该“答案”太大了,需缩小范围。
同时借助并查集维护连通性。
在这里插入图片描述
若两点间的距离小于半径r,则将其连通。
可行情况举例:
在这里插入图片描述
不可行情况举例:
在这里插入图片描述

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=1e4+10;
const ll mod=1000000007;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
inline ll lowbit(ll x){return x&(-x);}
int n,m,k;
int fa[maxn];
int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
struct dots{
	double x,y;
}a[maxn];
bool check(double r){
	for(int i=1;i<=k+4;i++) fa[i]=i;
	for(int i=1;i<=k;i++){
		if(m-a[i].y<r){//如果该点与上边界的距离小于半径,则将该点与上边界连通 
			int u=find(i);
			int v=find(k+1);
			if(u!=v) fa[u]=v;
		}
		if(a[i].y<r){//如果该点与下边界的距离小于半径,则将该点与下边界连通
			int u=find(i);
			int v=find(k+2);
			if(u!=v) fa[u]=v;
		}
		if(a[i].x<r){//如果该点与左边界的距离小于半径,则将该点与左边界连通
			int u=find(i);
			int v=find(k+3);
			if(u!=v) fa[u]=v;
		}
		if(a[i].x>n-r){//如果该点与右边界的距离小于半径,则将该点与右边界连通
			int u=find(i);
			int v=find(k+4);
			if(u!=v) fa[u]=v;
		}
		for(int j=1;j<i;j++){
			double dis=(a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y);
			if(dis<4*r*r){//如果两点间的距离小于直径,则将这两个点连通 
				int u=find(i);
				int v=find(j);
				if(u!=v) fa[u]=v;
			}
		} 
	}
	if(find(k+1)==find(k+2)||find(k+3)==find(k+4)||find(k+1)==find(k+4)||find(k+3)==find(k+2)) return 0;
	return 1;
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=k;i++){
		scanf("%lf%lf",&a[i].x,&a[i].y);
	}
	double l=0,r=max(n,m);
	while(r-l>=1e-6){//二分法搜答案 
		double mid=(l+r)/2.0;
		if(check(mid)) l=mid;
		else r=mid;
	}
	printf("%.6lf\n",l);
    return 0;
}

D. Jogging

题目链接

题目大意:
在街道上进行慢跑,问在满足跑步距离上下限要求的情况下,最多能跑多少条不同的路线。

每次跑上一条之前没跑过的边,即可视为跑的是一条不同的路线。
所以就是看最多能到达的边的数量,
而一条边有两个端点,如果到达两个端点的最短距离的最小值的两倍小于跑步距离的上限,则说明能达到该边,数量加一。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const ll mod=1000000007;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
inline ll lowbit(ll x){return x&(-x);}
vector<P>v;//记录边的两个端点
int cnt,e[maxn],w[maxn],ne[maxn],h[maxn];//链式前向星存图
void add(int x,int y,int z){
	e[cnt]=y;
	w[cnt]=z;
	ne[cnt]=h[x];
	h[x]=cnt++;
}
int dis[maxn],vis[maxn];
void dij(){//dij算法求解从起点0到各端点的最短距离
	memset(dis,0x3f,sizeof(dis));
	priority_queue<P,vector<P>,greater<P> >q;
	q.push(make_pair(0,0));
	dis[0]=0;
	while(!q.empty()){
		int p=q.top().second;
		q.pop();
		if(vis[p]) continue;
		vis[p]=1;
		for(int i=h[p];i!=-1;i=ne[i]){
			int tmp=e[i];
			if(dis[tmp]>dis[p]+w[i]){
				dis[tmp]=dis[p]+w[i];
				q.push(make_pair(dis[tmp],tmp));
			}
		}
	}
}
int main(){
	memset(h,-1,sizeof(h));
	int n,m,l,r;
	scanf("%d%d%d%d",&n,&m,&l,&r);
	while(m--){
		int x,y,w;
		scanf("%d%d%d",&x,&y,&w);
		v.push_back(make_pair(x,y));
		add(x,y,w);
		add(y,x,w);
	}
	dij();
	int ans=0;
	for(int i=0;i<v.size();i++){
		if(2*min(dis[v[i].fi],dis[v[i].se])<r) ans++;
	}
	printf("%d\n",ans);
	return 0;
} 

E. Cakes

题目链接
水题~

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e4+4;
int main(){
	int n;
	cin>>n;
	int ans=1000000;
	int a,b,c;
	for(int i=0;i<n;i++){
		cin>>a>>b;
		c=b/a;
		ans=min(ans,c);
	}
	cout<<ans<<endl;
	return 0;
}

K. Unique Activities

题目链接

题目大意:
寻找值出现过一次且出现位置最靠前的子字符串。

二分字符串长度,枚举该长度的子字符串,统计其出现次数。
但如果直接暴力的去取子字符串,出现了内存超限的情况。
此题正解需要用到字符串哈希(新知识get√)

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
const ll mod=1000000007;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);}
const int seed=13331;
ull p[maxn];
ull ha[maxn];
unordered_map<ull,int>mp;
string s;
void init(){
	p[0]=1;
	for(int i=1;i<=maxn;i++) p[i]=p[i-1]*seed;
}
bool check(int len){
	int num=0;
	mp.clear();
	for(int i=1;i<s.size();i++){
		int y=i+len-1;
		if(y>=s.size()) break;
		int x=i;
		ull tmp=ha[y]-ha[x-1]*p[len];//获取字符串的哈希值
		mp[tmp]++;
		if(mp[tmp]==1) num++;
		if(mp[tmp]==2) num--;
	}
	return num;
}
int main(){
	init();
	cin>>s;
	s="."+s;
	for(int i=1;i<s.size();i++){//字符串哈希
		ha[i]=ha[i-1]*seed+s[i]-'A'+1;
	}
	int l=1,r=s.size()-1;
	while(l<r){
		int mid=(l+r)/2;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	mp.clear();
	for(int i=1;i<s.size();i++){
		int y=i+l-1;
		if(y>=s.size()) break;
		int x=i;
		ull tmp=ha[y]-ha[x-1]*p[y-x+1];
		mp[tmp]++;
	}
	for(int i=1;i<s.size();i++){
		int y=i+l-1;
		if(y>=s.size()) break;
		int x=i;
		ull tmp=ha[y]-ha[x-1]*p[y-x+1];
		if(mp[tmp]==1){
			cout<<s.substr(x,l)<<endl;
			break;
		}		
	}
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值