快速跳转
题目类型
- 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