2020ICPC·小米 网络选拔赛第一场 全部题解

题目类型

  • A:DP+一点点数论(几乎不用)
  • B:最短路+计算几何(判断线段相交)
  • C:签到题
  • D:图论(tarjan求连通块)
  • E:数据结构(线段树)
  • F:二分答案
  • G:看着像图论,其实构造就行了
  • H:数学推导
  • I:搜索(标程好像是bfs,但我是用dfsA的)
  • J:前缀和+差分
  • K:数学

A-Intelligent Warehouse

思路:
给定一组数,选出最多的数,保证其中任意两个数有 a i a_{i} ai a j a_{j} aj的因子或 a j a_{j} aj a i a_{i} ai的因子。
称选出的最多的数为数群(暂且这么叫,因为其中可以有相同元素,所以不能称作数集)。
考虑DP,对一个数 a i a_{i} ai d p [ a i ] dp[a_{i}] dp[ai]为满足条件且最大数为 a i a_{i} ai的数群,那么对这个数群中 a i a_{i} ai最大的因子 a j a_{j} aj,必满足数群中 a i a_{i} ai其他的因子也是 a j a_{j} aj的因子,就有 d p [ a i ] = m a x ( d p [ a j ] , d p [ a i ] ) dp[a_{i}]=max(dp[a_{j}],dp[a_{i}]) dp[ai]=max(dp[aj],dp[ai]),然后直接暴力即可。
注意:
选择的数可以有重复,所以要记得计数,比赛时我就是当成数集导致WA了好几次,这个做法刚好没TLE,官方似乎有更快的做法。

#include<bits/stdc++.h>
using namespace std;
const int N=1e7;
int cnt[N+5],dp[N+5];
int main(){
   
	int n,num,ans=0;
	cin>>n;
	for(int i=1;i<=n;i++){
   
		scanf("%d",&num);
		cnt[num]++; 
	}
	for(int i=1;i<=N;i++)
		if(cnt[i]){
   
			dp[i]+=cnt[i];
			for(int j=i*2;j<=N;j+=i){
   
				dp[j]=max(dp[i],dp[j]);
			}
			ans=max(ans,dp[i]);
		}
	cout<<ans;
	return 0;
}

B-Intelligent Robot

思路:
注意两点之间直线最短,所以最优路线永远是走线段端点,不需要在线段上走,那么只需要把所有端点(包括起点终点)连接起来,删去相交且交点不在端点的线段,就可以变成最短路问题,然后用dijkstra算法求解
判断线段相交先跨立试验后叉乘,但这题可以直接用叉乘(想一想为什么?)
注意:
这里线段相交,但交点是两线段其中之一的端点的线段不用删去。

#include<bits/stdc++.h>
using namespace std;
const int INF=1e18,N=1e6;
struct edge{
   
	int x1,y1,x2,y2;
}e[N];
struct point{
   
	int x,y;
}p[N];
point operator-(point a,point b){
   return (point){
   a.x-b.x,a.y-b.y};}
int operator *(const point &a,const point &b)
{
   
    int t=a.x*b.y-b.x*a.y;
    return t<0?-1:(t>0);
}
struct heap{
   
	double d;
	int u;
	bool operator<(const heap& temp) const{
   
		return d>temp.d;
	}
};
priority_queue<heap>Q;
bool line(point a1,point a2,point b1,point b2){
   
	return((a2-a1)*(b1-a1))*((a2-a1)*(b2-a1))<0&&((b2-b1)*(a1-b1))*((b2-b1)*(a2-b1))<0;
}
int n,m,k,first[N],qnext[N],v[N],esum=0;
double w[N],d[N];
bool vis[N];
void add(int a,int b,double c){
   
	v[esum]=b;
	w[esum]=c;
	qnext[esum]=first[a];
	first[a]=esum++;
}
int main(){
   
	memset(first,-1,sizeof(first));
	cin>>n>>m>>k;
	for(int i=1;i<=k;i++){
   
		cin>>e[i].x1>>e[i].y1>>e[i].x2>>e[i].y2;
		p[i*2-1].x=e[i].x1,p[i*2-1].y=e[i].y1;
		p[i*2].x=e[i].x2,p[i*2].y=e[i].y2;
	}
	cin>>p[0].x>>p[0].y>>p[2*k+1].x>>p[2*k+1].y;
	bool flag;
	for(int i=0;i<2*k+1;i++)
		for(int j=i+1;j<=2*k+1;j++){
   
			flag=1;
			for(int q=1;q<=k;q++)
				if(line(p[i],p[j],(point){
   e[q].x1,e[q].y1},(point){
   e[q].x2,e[q].y2})){
   
					flag=0;
					break;
				}
			if(flag){
   
				double dis=sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y));
				v[esum]=j;
				w[esum]=dis;
				qnext[esum]=first[i];
				first[i]=esum++;
				v[esum]=i;
				w[esum]=dis;
				qnext[esum]=first[j];
				first[j]=esum++;
			}
		}
	for(int i=1;i<=2*k+1;i++) d[i]=INF;
	Q.push((heap){
   0,0});
	while(!Q.empty()){
   
		heap x=Q.top();
		Q.pop();
		int u=x.u;
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=first[u];~i;i=qnext[i])
			if(d[v[i]]>d[u]+w[i]){
   
				d[v[i]]=d[u]+w[i];
				Q.push((heap){
   d[v[i]],v[i]});
			}
	}
	printf("%.4f",d[k*2+1]);
	return 0; 
} 

C-Smart Browser

签到题,队友写的,略

D-Router Mesh

思路:
题目很显然是求连通块,用tarjan算法即可
注意:
开始给的图可能不是连通的

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,s,t) for(int i=s;i>=t;i--)
const int N=3e5+5;
int fst[N],nxt[N*2],to[N*2],dfn[N],low[N],ans[N];
int n,m,ti=0,ecnt=0,sum=0,r;
bool vis[N],cut[N];
void add(int x,int y){
   
	nxt[++ecnt]=fst[x];
	fst[x]=ecnt;
	to[ecnt]=y;
}
void tarjan(int x){
   
	dfn[x]=low[x]=++ti;
	int c=0,cnt=0;
	for(int i=fst[x];~i;i=nxt[i])
		if(!dfn[to[i]]){
   
			tarjan(to[i]);
			low[x]=min(low[x],low[to[i]]);
			if(low[to[i]]>=dfn[x]){
   
				c++;
				cnt++;
				if(x!=r||c>1) cut[x]=1;
			}
		}
		else low[x]=min(low[x],dfn[to[i]]);
	if(x!=r) cnt++;
	ans[x]=cnt;
} 
int main(){
   
	cin>>n>>m;
	memset(fst,-1,sizeof(fst));
	memset(nxt,-1,sizeof(nxt));
//	rep(i,1,n) fa[i]=i;
	int u,v;
	rep(i,1,m){
   
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	rep(i,1,n)
		if(!dfn[i]){
   
			sum++;
			tarjan(r=i);
		}
	rep(i,1,n)
		cout<<ans[i]+sum-1<<" ";
	return 0;
}

E-Phone Network

思路:
R i , j R_{i,j} Ri,j是以j为左端点,包含1~i所以数字的序列的最小右端点,考虑 R i + 1 , j R_{i+1,j} Ri+1,j,令所以i+1的下标为 p 1 , p 2 , . . . , p s p_{1},p_{2},...,p_{s} p1,p2,...,ps

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值