[NOIP2018模拟10.15]比赛报告
闲扯
昨晚又颓到好晚,Yali的降智光环感觉持续至今...
题面好评 T1T3都玩过 逃)
T1没看多久就开始写二分+并查集 然后T3看着眼熟想了一个多小时...结果啥都没想出来
赶紧看T2发现还是没什么思路,码个暴力回来看T1,发现了两个致命又SB的错误,倒数15分钟前终于改回来,刺激
结果80+35+0 T1还是挂分了,检查时发现还是一个思博错误,没有判上下相连与左右相连情况,感谢出题人良心数据
T2调了好久结果爆栈RE,不想改了。T3听完晚上的讲评后才茅塞顿开,太菜了
T1 刺客信条 AC
分析
我的做法很naiive,直接距离,将每个人看成圆心画一个二分距离的圆,两个圆有交就连在一起,最后判断Ezio能不能过去.
这里的判读大佬们都是将4面墙看成4个点处理,我最SB,每个点开四个bool变量记录,合并时暴力合并bool值
std求了个最小生成树,比较神奇
然后加了一些优化,预处理点对距离,特判之类的一开始跑了个rank4
代码
/*
code by RyeCatcher
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <utility>
#include <cmath>
#include <queue>
#include <vector>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <iostream>
#define DEBUG freopen("dat.in","r",stdin);freopen("wa.out","w",stdout);
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define ri register int
#define ll long long
#define ull unsigned long long
#define SIZE 1<<22
using std::sqrt;
using std::min;
using std::max;
using std::priority_queue;
using std::queue;
using std::vector;
using std::pair;
using namespace __gnu_pbds;
inline char gc(){
static char buf[SIZE],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while((c=gc())>'9'||c<'0')ne=c=='-';x=c-48;
while((c=gc())>='0'&&c<='9')x=(x*10)+c-48;x=ne?-x:x;return ;
}
const int maxn=3005;
const int inf=0x7fffffff;
const long double eps=1e-7;
struct Pt{
long double x,y;
}pt[maxn];
long double X,Y;
int fa[maxn];
struct Flag{
bool f1,f2,f3,f4;
inline void clear(){f1=f2=f3=f4=0;}
}ff[maxn];
inline void merge(int x,int y){
if(ff[x].f1|ff[y].f1)ff[x].f1=ff[y].f1=1;
if(ff[x].f2|ff[y].f2)ff[x].f2=ff[y].f2=1;
if(ff[x].f3|ff[y].f3)ff[x].f3=ff[y].f3=1;
if(ff[x].f4|ff[y].f4)ff[x].f4=ff[y].f4=1;
return ;
}
int get(int x){
merge(x,fa[x]);
if(fa[x]!=x){
fa[x]=get(fa[x]);
}
return fa[x];
}
bool vis[maxn];
long double fafa[maxn][maxn];
int n;
bool check(long double Dis){
for(ri i=1;i<=n;i++){
vis[i]=0;
fa[i]=i;
ff[i].clear();
}
long double x,y;
for(ri i=1;i<=n;i++){
int cnt=0;
x=pt[i].x,y=pt[i].y;
if(Dis-x>1e-11)ff[i].f1=1,cnt++;
if(Dis-y>1e-11)ff[i].f2=1,cnt++;
if(Dis-(X-x)>1e-11)ff[i].f3=1,cnt++;
if(Dis-(Y-y)>1e-11)ff[i].f4=1,cnt++;
//if(cnt>=2)return 1;
if(ff[i].f1&&ff[i].f2)return 1;
if(ff[i].f3&&ff[i].f4)return 1;
}
int fx,fy;
for(ri i=1;i<=n;i++){
x=pt[i].x,y=pt[i].y;
for(ri j=i+1;j<=n;j++){
long double p=fafa[i][j];
if(Dis*2-p>1e-11){
fx=get(i),fy=get(j);
fa[fx]=fy;
merge(fx,fy);
}
}
}
for(ri i=1;i<=n;i++){
fx=get(i);
if(vis[fx])continue;
vis[fx]=1;
int tmp=ff[fx].f1+ff[fx].f2+ff[fx].f3+ff[fx].f4;
if(ff[fx].f1&&ff[fx].f2)return 1;
if(ff[fx].f3&&ff[fx].f4)return 1;
if(ff[fx].f2&&ff[fx].f4)return 1;
if(ff[fx].f1&&ff[fx].f3)return 1;
}
return 0;
}
int main(){
long double x,y;
//freopen("AC.in","r",stdin);
//freopen("AC.out","w",stdout);
read(X),read(Y),read(n);
for(ri i=1;i<=n;i++){
read(pt[i].x),read(pt[i].y);
}
for(ri i=1;i<=n;i++){
x=pt[i].x,y=pt[i].y;
for(ri j=i+1;j<=n;j++){
fafa[i][j]=sqrtl((x-pt[j].x)*(x-pt[j].x)+(y-pt[j].y)*(y-pt[j].y));
}
}
long double mid,L=0,R=sqrtl(X*X+Y*Y+233);
while(R-L>eps){
mid=(L+R)/2;
if(check(mid))R=mid;
else L=mid;
//printf("%.4lf %.4lf\n",L,R);
}
printf("%.2Lf\n",R);
return 0;
}
T2 黑暗之魂 darksoul
分析
先不考虑自环和重边,最终的图像肯定是一个环,环上若干点,点可能向外扩展成一棵树
假如最终答案在一颗树中,我们就要求出树上相距最远的两点,用树形DP即可,
\(g[x]\)表示向下在以x为根的子树中最远能扩展到哪里,\(o[x]\)表示次远值
g[x]=max(g[x],g[v]+dis(x,v)),o[x]就不赘述了
然后以x为LCA的两点最远值f[x]=g[x]+o[x],然后\(max_{x \in T}(f[x])\)就是树T的贡献
但是如果答案的路径经过了环上的边呢,对于环上路径\((x,y)\). (x,y都是环上的点)
它的贡献为\(g[x]+g[y]+dis(x,y)\)
\(g\)值我们是已经求出来的,但是\(dis\)怎么求?我们化环为链,钦定起点\(st\),用前缀和数组\(pre[x]\)表示\(dis(st,x)\)
那么\(dis(x,y)\)就是\(pre[x]-pre[y]\)(设\(x\)在\(y\)之后),注意我们要倍长这条链
这样贡献就变成了\(g[x]+pre[x]+g[y]-pre[y]\)
然而需要注意的是如果\(pre[x]-pre[y]\)大于环周长的一半是不合法的,为啥?因为他总是选择最短路走,既然这段大于周长一半,反过来走肯定更短
暴力的做法就是N方环上每一对点枚举一遍,考虑高级一点的做法,发现化环为链后处理的\(pre\)数组是单调递增的,
于是维护一个\(g[y]-pre[y]\)值递减的滑动窗口(因为当前点为\(x\),\(g[x]+pre[x]\)是固定的),一旦队头\(p\)到当前点距离,即\(pre[x]-pre[p]\)大于两倍周长就弹出队头
然后大佬们都是用拓扑排序搞,我只会naiive的深搜,然后交上去最后两个点爆栈了
同时还发现我没有考虑答案是在一棵树中的情况,只能说这数据水了...
代码
/*
code by RyeCatcher
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <utility>
#include <queue>
#include <vector>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <iostream>
#define DEBUG freopen("dat.in","r",stdin);freopen("wa.out","w",stdout);
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define ri register int
#define ll long long
#define ull unsigned long long
#define SIZE 1<<22
using std::min;
using std::max;
using std::priority_queue;
using std::deque;
using std::vector;
using std::pair;
using namespace __gnu_pbds;
inline char gc(){
static char buf[SIZE],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2)?EOF:*p1++;
}
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while((c=gc())>'9'||c<'0')ne=c=='-';x=c-48;
while((c=gc())>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48;x=ne?-x:x;return ;
}
const int maxn=2000005;
const int inf=0x7fffffff;
struct Edge{
int ne,to;
ll dis;
}edge[maxn<<1];
int h[maxn];ll num_edge=1;
inline void add_edge(int f,int to,ll c){
edge[++num_edge].ne=h[f];
edge[num_edge].to=to;
edge[num_edge].dis=c;
h[f]=num_edge;
}
int n;
int fa[maxn],dep[maxn];
bool vis[maxn],flag_1;
int fo1,fo2;
void pre_dfs(int now,int fa){
int v;
vis[now]=1;
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fa)continue;
if(vis[v]&&dep[v]==dep[now]+1){
//puts("11");
for(ri k=h[now];k;k=edge[k].ne){
//printf("%d\n",edge[k].to);
if(edge[k].to==v){
if(!fo1)fo1=k;
else fo2=k;
}
}
flag_1=1;return ;
}
else if(vis[v])continue;
dep[v]=dep[now]+1;
pre_dfs(v,now);
}
return;
}
namespace Tree{
ll Tmp=-1,ans=-1;
int rt=1;
void dfs_1(int now,int fa,ll dis){
int v;
if(Tmp<dis){
rt=now,Tmp=dis;
}
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(i==fo1||i==fo2)continue;
if(v==fa||v==now)continue;
dfs_1(v,now,dis+edge[i].dis);
}
return ;
}
void dfs_2(int now,int fa,ll dis){
int v;
ans=max(ans,dis);
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(i==fo1||i==fo2)continue;
if(v==fa||v==now)continue;
dfs_2(v,now,dis+edge[i].dis);
}
return ;
}
void main(){
if(edge[fo1].dis<edge[fo2].dis){
fo1=fo2^1;
}
else{
fo2=fo1^1;
}
dfs_1(1,0,0);
Tmp=-1;
dfs_2(rt,0,0);
printf("%lld\n",ans+1);
return ;
}
}
int sta[maxn],cnt=0,st,ed,len=0;
bool on_cyc[maxn];
void find_cyc(int now){
int v;
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fa[now])continue;
if(dep[v]&&dep[v]<dep[now]){
st=now,ed=v;
int x=now;
while(x!=v)len++,on_cyc[x]=1,sta[++cnt]=x,x=fa[x];
len++,on_cyc[v]=1,sta[++cnt]=v;
continue;
}
else if(dep[v])continue;
fa[v]=now;
dep[v]=dep[now]+1;
find_cyc(v);
}
return ;
}
int rt;
ll dm[maxn];
ll tmp=-1,cc=0;
ll dp[maxn];
void dfs_1(int now,int fa){
int v;
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(on_cyc[v]||v==fa)continue;
dfs_1(v,now);
dp[now]=max(dp[now],dp[v]+edge[i].dis);
}
return ;
}
ll pre[maxn];//dist from st
int ff[maxn],ne[maxn],tot=0;
void dfs_on_cyc(int now,int fa){
int v;
if(tot==len*2-1)return ;
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v!=ne[now])continue;
pre[tot+1]=pre[tot]+edge[i].dis;
ff[++tot]=v;
if(tot==len+1)cc=pre[tot];
dfs_on_cyc(v,now);
}
return ;
}
deque <int> q;
ll arr[maxn];
int main(){
bool flag=0;
int x,y;ll z;
FO(darksoul);
//freopen("darksoul19.in","r",stdin);
read(n);
//puts("sss");
//system("PAUSE");
for(ri i=1;i<=n;i++){
read(x),read(y),read(z);
add_edge(x,y,z);
add_edge(y,x,z);
if(x==y)flag=1;
}
dep[1]=1;
pre_dfs(1,0);
if(flag||flag_1){
//puts("ss");
Tree::main();return 0;
}
//memset(vis,0,sizeof(vis));
memset(dep,0,sizeof(dep));
fa[1]=0,dep[1]=1;
find_cyc(1);
for(ri i=1;i<=cnt;i++){
rt=sta[i];
if(i!=cnt)ne[sta[i]]=sta[i+1];
else ne[sta[i]]=sta[1];
tmp=-1;
dfs_1(rt,0);
dm[sta[i]]=dp[rt];
}
tot=1,ff[1]=sta[1];
dfs_on_cyc(st,0);
for(ri i=1;i<=len*2-1;i++){
arr[i]=dm[ff[i]]-pre[i];
}
ll ans=-1;
for(ri k=1;k<=len*2-1;k++){
if(q.empty())q.push_back(k);
else{
while(q.size()&&pre[k]-pre[q.front()]>cc/2)q.pop_front();
ans=max(ans,dm[ff[k]]+pre[k]+arr[q.front()]);
while(q.size()&&arr[q.back()]<=arr[k])q.pop_back();
q.push_back(k);
}
}
printf("%lld\n",ans+1);
return 0;
}
T3 传送门 portal
分析
巧妙的树形DP
假设当前我们正在\(x\)点,\(y\)是\(x\)的一个儿子,\(f[x]\)表示以\(x\)为根的子树的最优答案,
那么我们考虑假如传送门在\(y\),那么\(y\)的贡献就是\(f[y]+c*2\),因为\(x,y\)还得靠你自己走
假如一个传送门在\(x\),那么\(y\)的贡献为\(sum_e(y) \times 2-g(y)+c\),\(sum_e(y)\)表示\(y\)的子树中边权之和,\(g(y)表示\)\(y\)的子树中的最长链长度.
这时候有人会问一个问题,你这样为什么不每次走到底再传送到x然后再经过\(c\),但是这样的话要经过多次\(c\),为什么不干脆将传送门设在\(y\),这样的贡献肯定不会比你那样走更多,所以我们这时候要选择最长链跳,其余的都靠步行的方式计算贡献
综上\(f[x]= \sum min(f[y]+c \times 2,sum_e(y) \times 2-g(y)+c)\)
由于我们能够安排儿子的\(dfs\)顺序,可知儿子之间是不会互相影响的
代码
/*
code by RyeCatcher
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <utility>
#include <queue>
#include <vector>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <iostream>
#define DEBUG freopen("dat.in","r",stdin);freopen("wa.out","w",stdout);
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define ri register int
#define ll long long
#define ull unsigned long long
#define SIZE 1<<22
using std::min;
using std::max;
using std::priority_queue;
using std::queue;
using std::vector;
using std::pair;
using namespace __gnu_pbds;
inline char gc(){
static char buf[SIZE],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while((c=gc())>'9'||c<'0')ne=c=='-';x=c-48;
while((c=gc())>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48;x=ne?-x:x;return ;
}
const int maxn=1000005;
const int inf=0x7fffffff;
int n;
struct Edge{
int ne,to;
ll dis;
}edge[maxn<<1];
int h[maxn],num_edge=1;
inline void add_edge(int f,int to,ll c){
edge[++num_edge].ne=h[f];
edge[num_edge].to=to;
edge[num_edge].dis=c;
h[f]=num_edge;
}
ll g[maxn],s[maxn],f[maxn];
void dfs(int now,int fa){
int v;ll c;
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fa)continue;
dfs(v,now);
c=edge[i].dis;
s[now]=s[now]+s[v]+c;
g[now]=max(g[now],g[v]+c);
f[now]+=min(s[v]*2-g[v]+c,f[v]+c*2);
}
return ;
}
int main(){
int x,y,z;
FO(portal);
read(n);
for(ri i=1;i<n;i++){
read(x),read(y),read(z);
add_edge(x,y,z);
add_edge(y,x,z);
}
dfs(1,0);
printf("%lld\n",f[1]);
return 0;
}