A. 【NOIP2018模拟赛】刺客信条(AC)
这道题第一眼看上去可能会想到找路径然后算距离,但发现这是不现实的。
所以我们通过限定答案的值来判断是否满足。
将答案设为 r (半径) ,每个棋盘上的点都由半径做圆,然后判断路径不触碰到圆的最大半径。
我们利用并查集将每个圆与墙进行处理,然后通过二分答案进行判断。
p.s. 记得要加eps (一个极小的数,调整精度)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef double db; 4 5 const db e=1e-4; 6 const int N=2011; 7 int n,m,k,fa[N]; 8 db x[N],y[N],dis[N][N]; 9 10 int getf(int x){ 11 if(fa[x]==x) return x; 12 else return fa[x]=getf(fa[x]); 13 } 14 15 void exchange(int x,int y){ 16 fa[getf(x)]=getf(y); 17 } 18 19 db two(db a){ 20 return a*a; 21 } 22 23 db d(int i,int j){ 24 return sqrt(two(x[i]-x[j])+two(y[i]-y[j])); 25 } 26 27 bool pd(double r){ 28 for(int i=1;i<=k+4;i++) fa[i]=i; 29 for(int i=1;i<=k;i++){ 30 if(r-x[i]>e) exchange(k+1,i);//左墙 31 if(r-y[i]>e) exchange(k+2,i);//底墙 32 if(r-(n-x[i])>e) exchange(k+3,i);//右墙 33 if(r-(m-y[i])>e) exchange(k+4,i);//顶墙 34 for(int j=i+1;j<=k;j++){ 35 if(r*2-dis[i][j]>e || (fabs(r*2-dis[i][j])<e)) exchange(i,j); 36 } 37 } 38 if(getf(k+1)==getf(k+2)) return true; 39 if(getf(k+1)==getf(k+3)) return true; 40 if(getf(k+2)==getf(k+4)) return true; 41 if(getf(k+3)==getf(k+4)) return true; 42 return false; 43 } 44 45 int read(){ 46 int x=0,f=1; 47 char s=getchar(); 48 while(!isdigit(s)) f*=(s=='-')? -1:1,s=getchar(); 49 do x=(x<<1)+(x<<3)+(s^48),s=getchar(); 50 while(isdigit(s)); 51 return x*f; 52 } 53 54 int main(){ 55 n=read(),m=read(),k=read(); 56 for(int i=1;i<=k;i++){ 57 scanf("%lf%lf",&x[i],&y[i]); 58 } 59 for(int i=1;i<=k;i++){ 60 for(int j=i+1;j<=k;j++){ 61 dis[i][j]=d(i,j); 62 } 63 } 64 db l=0,r=1000000; 65 while(r-l>e){ 66 db mid=(l+r)/2; 67 if(pd(mid)) r=mid; 68 else l=mid; 69 } 70 printf("%.2f",l); 71 return 0; 72 }
C. 【NOIP2018模拟赛】传送门 (portal)
一道树形dp的题。
设f[][]表示状态
f[x][0]:x和x的子树里没有传送门
f[x][1]:x和x的子树里有传送门
状态转移方程:
f[x][0]+=f[y][0]+2*w[i];
f[x][1]+=min(f[y][0]+w[i]-d[y],f[y][1]+w[i]*2);
code
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=1e6+5; 4 const long long null=1e15; 5 6 int n; 7 int head[MAXN],to[MAXN*2],next[MAXN*2],w[MAXN*2],cnt; 8 long long f[MAXN][2],d[MAXN]; 9 10 void add(int u,int v,int l){ 11 cnt++; 12 next[cnt]=head[u]; 13 to[cnt]=v; 14 w[cnt]=l; 15 head[u]=cnt; 16 } 17 18 void dp(int x,int fa){ 19 for(int i=head[x];i;i=next[i]){ 20 int y=to[i]; 21 if(y==fa) 22 continue; 23 dp(y,x); 24 d[x]=max(d[x],d[y]+w[i]); 25 f[x][0]+=f[y][0]+2*w[i]; 26 f[x][1]+=min(f[y][0]+w[i]-d[y],f[y][1]+w[i]*2); 27 } 28 } 29 30 int main(){ 31 cin>>n; 32 for(int i=1;i<n;i++){ 33 int u,v,a; 34 scanf("%d%d%d",&u,&v,&a); 35 add(u,v,a); 36 add(v,u,a); 37 } 38 dp(1,1); 39 cout<<min(f[1][1],f[1][0]); 40 return 0; 41 }
B. 【NOIP2018模拟赛】黑暗之魂(darksoul)
这是一颗环套树
题目要求的是这棵环套树的直径,再+1就是答案了。
我们把该环提出来,在上面操作。
那么直径要么是环上某点的子树内直径,要么是环上某两点子树内最长链再加一段环上路径。
子树内直径和最长链我们先算出来,剩下的就是如何求出环上两点使之匹配出来最大值。
枚举环上一点 i ,同时维护一个指针 j 往后指,表示从 i+1 到 j 是近一些,而 j 之后的点呢会绕环一圈来走回 i 更近一些。
那么 i+1 到 j的我们用一个单调队列来求最大值,绕一周的用一个后缀最大值算就好了。
1 #include<cstdio> 2 #include<cctype> 3 using namespace std; 4 typedef long long ll; 5 const int N=1e6+5; 6 int tot=1,top,qx,qy,node; 7 ll ans,mx; 8 bool pd; 9 int first[N],nex[N<<1],en[N<<1],w[N<<1]; 10 int st[N],dfn[N],d[N],len[N],col[N],q[N]; 11 ll suf[N],pre[N],f[N]; 12 bool bz[N]; 13 inline int read(){ 14 int x=0,f=1; 15 char s=getchar(); 16 while(!isdigit(s)) f*=(s=='-')? -1:1,s=getchar(); 17 do x=(x<<1)+(x<<3)+(s^48),s=getchar(); 18 while(isdigit(s)); 19 return x*f; 20 } 21 inline ll max(ll x,ll y) 22 { 23 return x>y?x:y; 24 } 25 inline void in(int x,int y,int z) 26 { 27 nex[++tot]=first[x]; 28 first[x]=tot; 29 en[tot]=y; 30 w[tot]=z; 31 } 32 void dfs(int x,int y) 33 { 34 st[++top]=x; 35 dfn[x]=top; 36 for(int i=first[x];i && !pd;i=nex[i]) 37 if(i^y) 38 { 39 if(dfn[en[i]]) 40 { 41 for(int j=dfn[en[i]];j<=top;j++) 42 { 43 d[++d[0]]=st[j]; 44 bz[st[j]]=true; 45 } 46 pd=true; 47 return; 48 } 49 dfs(en[i],i^1); 50 } 51 top--; 52 } 53 void find(int x,int y,ll z,ll &num) 54 { 55 if(z>num) num=z; 56 for(int i=first[x];i;i=nex[i]) 57 if(en[i]^y && en[i]^x) find(en[i],x,z+w[i],num); 58 } 59 void get(int x,int y) 60 { 61 if(y==d[0]) return; 62 for(int i=first[x];i;i=nex[i]) 63 if(en[i]==d[y+1]) 64 { 65 len[y]=w[i]; 66 get(en[i],y+1); 67 } 68 } 69 void dg(int x,int y,ll z) 70 { 71 if(z>mx) mx=z,node=x; 72 col[x]=tot; 73 for(int i=first[x];i;i=nex[i]) 74 if(en[i]^y && en[i]^x && !bz[en[i]]) dg(en[i],x,z+w[i]); 75 } 76 void dg1(int x,int y,ll z) 77 { 78 if(z>mx) mx=z,node=x; 79 for(int i=first[x];i;i=nex[i]) 80 if(en[i]^y && en[i]^x && col[en[i]]==tot) dg1(en[i],x,z+w[i]); 81 } 82 int main() 83 { 84 int n=read(); 85 for(int i=1;i<=n;i++) 86 { 87 int x=read(),y=read(),z=read(); 88 in(x,y,z); 89 in(y,x,z); 90 } 91 dfs(1,0); 92 tot=0; 93 for(int i=1;i<=d[0];i++) 94 { 95 for(int j=first[d[i]];j;j=nex[j]) 96 if(!bz[en[j]]) find(en[j],d[i],w[j],f[i]); 97 node=mx=0; 98 tot++; 99 dg(d[i],0,0); 100 dg1(node,0,0); 101 ans=max(ans,mx); 102 if(f[i]>ans) ans=f[i]; 103 } 104 get(d[1],1); 105 for(int i=first[d[d[0]]];i;i=nex[i]) 106 if(en[i]==d[1]) 107 { 108 len[d[0]]=w[i]; 109 break; 110 } 111 for(int i=2;i<=d[0];i++) pre[i]=pre[i-1]+len[i-1]; 112 for(int i=2;i<=d[0];i++) suf[i]=pre[d[0]]+len[d[0]]-pre[i]+f[i]; 113 for(int i=d[0]-1;i>1;i--) suf[i]=max(suf[i+1],suf[i]); 114 int l=1,r=0; 115 for(int i=1,j=1;i<d[0];i++) 116 { 117 while(l<r && q[l]<=i) l++; 118 while(j<d[0] && pre[j+1]-pre[i]<=pre[d[0]]-pre[j+1]+len[d[0]]+pre[i]) 119 { 120 j++; 121 while(l<=r && f[q[r]]+pre[q[r]]<=f[j]+pre[j]) r--; 122 q[++r]=j; 123 } 124 ll num=0; 125 if(j<d[0]) num=suf[j+1]+pre[i]; 126 if(i<j) num=max(num,f[q[l]]+pre[q[l]]-pre[i]); 127 ans=max(ans,num+f[i]); 128 } 129 printf("%lld",ans+1); 130 return 0; 131 }