T1
考试的时候连题面都没看懂,都没往图论这方面想,更别提最小生成树。
正解:
最小生成树prim,好像是什么欧几里得生成树,寒假时候的东西了,我直接找的blog看,没看见这么玩意,果然还是太菜了
平面欧几里得最小生成树(EMST)来自wiki百科
直接prim,套板子就好,把上下边界压成一个点,注意,在prim过程中,第一个for循环找出的点如果为压缩后的上边界,此时直接break就好,
prim跟点有关,kruskal跟边有关,此图为完全图,边数达到了 \(n^{2}\),显然用prim更优,用kruskal也可,就是慢很多。
Code#include<cmath>
#include<cstdio>
#include<iostream>
#define MAX 6010
#define INF 114514810
#define re register
namespace OMA
{
int n,m,k;
bool vis[MAX];
double ans,dis[MAX];
double x[MAX],y[MAX];
inline double Dis(int i,int j)
{ return (i>k||j>k||!i||!j)?sqrt((y[i]-y[j])*(y[i]-y[j])):sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])); }
inline void prim()
{
for(re int i=0; i<=k+1; i++)
{ dis[i] = INF; }
dis[0] = 0;
for(re int i=0; i<=k+1; i++)
{
int tmp = 0,top = INF;
for(re int j=0; j<=k+1; j++)
{
if(!vis[j]&&dis[j]<top)
{ tmp = j,top = dis[j]; }
}
if(tmp==k+1)
{ break ; }
vis[tmp] = true;
ans = std::max(ans,dis[tmp]);
for(re int j=0; j<=k+1; j++)
{ dis[j] = std::min(dis[j],Dis(j,tmp)); }
}
}
signed main()
{
scanf("%d %d %d",&n,&m,&k);
for(re int i=1; i<=k; i++)
{ scanf("%lf%lf",&x[i],&y[i]); }
y[k+1] = m,prim();
printf("%0.9lf\n",ans/2);
return 0;
}
}
signed main()
{ return OMA::main(); }
T2
不会dp,所以考试的时候,写的贪心,又写假贪心,改后的贪心没交上,假的骗了10pts,不要脸的说一下假贪心
将左边的点跟右边连起来的点用结构体存起来,按差值排一下序,差值越大,则交线越多,然后就从1开始暴搜,再多开一个二维数组 \(line_{i,j}\),表示此时是以i这条线开始删的,第j条线被删掉了,暴搜下一条线时,转移一下状态,转移答案时,判断是否全删完了,再转移。复杂度,应该是\(O(n^{3})\)
贪心#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAX 10010
#define re register
namespace OMA
{
int n,ans=0x3f3f3f3f;
int c[MAX];
inline int abs(int a)
{ return a>=0?a:-a; }
struct node
{
int l,r,c;
friend inline bool operator <(const node &a,const node &b)
{ return abs(a.l-a.r)>abs(b.l-b.r); }
}p[MAX];
int lr[MAX],rl[MAX];
bool line[MAX][MAX];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline int min(int a,int b)
{ return a<b?a:b; }
inline void dfs(int i,int res,int cast)
{
line[i][p[i].r] = true;
res -= abs(p[i].r-p[i].l)+1;
cast += p[i].c;
if(p[i].l>p[i].r)
{
for(re int j=p[i].r; j<=n; j++)
{ line[i][j] = true; }
}
else
{
for(re int j=1; j<=p[i].r; j++)
{ line[i][j] = true; }
}
bool flag = true;
for(re int j=1; j<=n; j++)
{
if(!line[i][j])
{ flag = false; }
}
if(flag&&res<=0)
{ ans = min(ans,cast); return ; }
for(re int j=1; j<=n; j++)
{
if(!line[i][p[j].r])
{
for(re int k=1; k<=n; k++)
{ line[j][k] = line[i][k]; }
dfs(j,res,cast);
}
}
}
inline void clear()
{ memset(line,0,sizeof(line)); }
signed main()
{
n = read();
for(re int i=1; i<=n; i++)
{ p[i] = (node){i,read()}; }
for(re int i=1; i<=n; i++)
{ p[i].c = read(); }
std::sort(p+1,p+1+n);
for(re int i=1; i<=n/2; i++)
{ dfs(i,n,0),clear(); }
printf("%d\n",ans);
return 0;
}
}
signed main()
{ return OMA::main(); }
正解线段树维护上升序列,咕了。
T3
暴力跳爹50pts。
50pts#include<cstdio>
#define MAX 500010
#define re register
namespace OMA
{
int n;
struct Graph
{
int next;
int to;
}edge[MAX<<1];
int dep[MAX];
int cnt=1,head[MAX];
int fa[MAX],bin[MAX];
double ans[MAX];
int c[MAX],dis[MAX];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline void add(int u,int v)
{
edge[++cnt].next = head[u];
edge[cnt].to = v;
head[u] = cnt;
}
inline double min(double a,double b)
{ return a<b?a:b; }
inline void dfs(int u,int fat)
{
fa[u] = fat;
int tmp = fa[u];
while(tmp)
{
ans[u] = min(ans[u],1.0*(c[tmp]-c[u])/(1.0*(dis[u]+dis[tmp]-2*dis[tmp])));
tmp = fa[tmp];
}
for(re int i=head[u]; i; i=edge[i].next)
{
int v = edge[i].to;
if(v!=fat)
{
dis[v] = dis[u]+1;
dfs(v,u);
}
}
}
signed main()
{
n = read();
for(re int i=2; i<=n; i++)
{ bin[i] = bin[i>>1]+1; }
for(re int i=1; i<=n; i++)
{ c[i] = read(); }
for(re int v=2; v<=n; v++)
{
ans[v] = 0x3f3f3f3f;
int u = read();
add(u,v),add(v,u);
}
dfs(1,0);
for(re int i=2; i<=n; i++)
{ printf("%0.10lf\n",ans[i]); }
return 0;
}
}
signed main()
{ return OMA::main(); }
/*
正解:
我们先将原式子转换一下
然后以c为y轴,dep为x轴,建立坐标系,发现这个式子其实就是在求斜率,那么考虑维护凸包,且只用维护下凸包,上凸包对答案造不成贡献。
暴力弹栈的话,会t成80pts精心构造的数据,所以考虑倍增弹栈或者说这叫可持久化栈
看了一个早上几何里的凸包
#include<cstdio>
#define MAX 500010
#define re register
namespace OMA
{
int n;
struct Graph
{
int next;
int to;
}edge[MAX<<1];
int cnt=1,head[MAX];
double ans[MAX];
int c[MAX],dep[MAX];
int fa[MAX][30],bin[MAX];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline void add(int u,int v)
{
edge[++cnt].next = head[u];
edge[cnt].to = v;
head[u] = cnt;
}
inline double work(int u,int v)
{ return 1.0*(c[u]-c[v])/(1.0*(dep[v]-dep[u])); }
inline void dfs(int u,int fat)
{
int anc = fat;
dep[u] = dep[fat]+1;
for(re int i=bin[dep[u]]; ~i; i--)
{
if(fa[anc][i]<=1)
{ continue ; }
if(work(fa[fa[anc][i]][0],u)<=work(fa[anc][i],u))
{ anc = fa[anc][i]; }
}
if(anc>1&&work(fa[anc][0],u)<=work(anc,u))
{ anc = fa[anc][0]; }
fa[u][0] = anc;
for(re int i=1; i<=bin[dep[u]]; i++)
{ fa[u][i] = fa[fa[u][i-1]][i-1]; }
for(re int i=head[u]; i; i=edge[i].next)
{
int v = edge[i].to;
if(v!=fat)
{ dfs(v,u); }
}
}
signed main()
{
n = read();
for(re int i=1; i<=n; i++)
{ c[i] = read(); }
for(re int v=2; v<=n; v++)
{
bin[v] = bin[v>>1]+1;
int u = read();
add(u,v),add(v,u);
}
dfs(1,0);
for(re int i=2; i<=n; i++)
{ printf("%0.10lf\n",work(fa[i][0],i)); }
return 0;
}
}
signed main()
{ return OMA::main(); }