NOIp2018集训test-9-17(am)

这是一套去年在长沙考过的题,但是我当时就没理清楚+没写题解(我以前很多博客都写得跟shi一样,完全没有意义,看到就想打当时的我),所以又考得稀烂。

T1.star way to heaven

容易想到二分+并查集,二分距离所有星星和边界的最小距离r,也就是距离这些点r以内的范围不能走,也就是看以每个点为圆心r为半径的圆能不能把上下都堵满。

这个做法会被卡成80分。正解是用最小生成树代替二分的过程。答案一定是两个点间的距离,最小生成树相当于是从小到大的枚举答案,可以满足当连接一条边权为w的边时,距离小于w的点都联通了,最后答案就是从上边界走到下边界的树上路径上的最大边权(去掉这条边就能不连通啦)。我写成了整颗树上的最大边权然后爆0了。

 1 //Achen
 2 #include<bits/stdc++.h>
 3 #define For(i,a,b) for(int i=(a);i<=(b);i++)
 4 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
 5 #define Formylove return 0
 6 const int N=6007;
 7 using namespace std;
 8 typedef long long LL;
 9 typedef double db;
10 int n,m,k;
11 db ans;
12 
13 template<typename T>void read(T &x) {
14     char ch=getchar(); T f=1; x=0;
15     while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
16     if(ch=='-') f=-1,ch=getchar();
17     for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
18 }
19 
20 struct node {
21     int x,y;
22 }p[N];
23 
24 LL pf(LL x) { return x*x; }
25 
26 LL get_dis(int a,int b) {
27     if(a>b) swap(a,b);
28     if(a<=n&&b<=n) return pf(p[a].x-p[b].x)+pf(p[a].y-p[b].y);
29     else if(a<=n&&b==n+1) return pf(m-p[a].y);
30     else if(a<=n&&b==n+2) return pf(p[a].y);
31     else if(a==n+1&&b==n+2) pf(m);
32 }
33 
34 int ecnt,fir[N],nxt[N<<1],to[N<<1];
35 LL val[N<<1];
36 void add(int u,int v,LL w) {
37     nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w;
38     nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; val[ecnt]=w;
39 }
40 
41 LL dfs(int x,int fa,LL mx) {
42     if(x==n+2) return mx;
43     for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) {
44         LL tp=dfs(to[i],x,max(mx,val[i]));
45         if(tp!=0) return tp;
46     }
47     return 0;
48 } 
49 
50 LL dis[N];
51 int pr[N];
52 int vis[N];
53 void prim() {
54     memset(dis,127/3,sizeof(dis));
55     dis[n+1]=0; 
56     For(i,1,n+2) {
57         int kk=0;
58         For(j,1,n+2) if(!vis[j]) {
59             if(!kk||dis[j]<dis[kk]) kk=j;
60         }
61         add(pr[kk],kk,dis[kk]);
62         dis[kk]=0;
63         vis[kk]=1;
64         For(j,1,n+2) if(!vis[j]) {
65             LL d=get_dis(j,kk);
66             if(dis[kk]+d<dis[j]) {
67                 pr[j]=kk;
68                 dis[j]=dis[kk]+d;
69             }
70         }
71     }
72 }
73 
74 #define ANS
75 int main() {
76 #ifdef ANS
77     freopen("starway.in","r",stdin);
78     freopen("starway.out","w",stdout);
79 #endif
80     read(n); read(m); read(k);
81     n=k;
82     For(i,1,k) {
83         read(p[i].x); 
84         read(p[i].y);
85     }
86     prim();
87     ans=dfs(n+1,0,0);
88     ans=sqrt(ans)/2.0;
89     printf("%.8lf\n",ans);
90     Formylove;
91 }
View Code

 

T2.God knows

这题好神啊。当年我在长沙看了一天才看懂(大概只是当时以为懂?), 然后现在根本不知道我当时在想/写些啥子。。

一句话题解就是,线段树维护单调栈优化dp。

把p[i]看成高度,删除一个点就是删掉所有它前面比它高的和它后面比他矮的,那么删除的一定是一个高度单增的序列,且满足不在序列里的数一定存在它前面高度比它高或者它后面高度比它矮的数在序列中,也就是说这是一个极长上升序列。f[i]表示选以i结尾的极长上升序列的答案,f[i]就是通过i之前的一个高度小于i的j,且j是j~i中比i矮的中最高的一个,这样的j来更新i,这样的j所在的是一个由i之前的比i矮的数组成的单调降的单调队列。

考虑如何维护这个单调队列?保证在i之前只要按i从小到大扫就好,要比i矮可以用线段树维护(一开始是标号从小到大对应高度从大到小的单调队列,现在线段树上高度从小到大,那么对应的就是一个标号从大到小的单调队列),那么现在只要在线段树一个区间内维护单降队列就ok了。 

线段树如何维护单调队列,参考楼房重建一题。还是解释一下代码以免以后看来想打自己,按高度建线段树,线段树上每个节点维护s[x],sl[x]两个pr,s[x]表示以当前区间做单调队列,(队列中的最大值,队列中的数的最小f值),sl[x]表示当前区间的左子区间在合并右子区间(合并后左边队列会在队尾上弹一些元素对吧)后的s的情况,用一个函数calc(l,r,x)表示l,r的区间做单调队列,最后入队的元素是x时队列的情况,这样就可以做了,具体参考代码。

 1 //Achen
 2 #include<bits/stdc++.h>
 3 #define For(i,a,b) for(int i=(a);i<=(b);i++)
 4 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
 5 #define Formylove return 0
 6 #define inf 0x7777777
 7 const int N=2e5+7;
 8 using namespace std;
 9 typedef long long LL;
10 typedef double db;
11 int n,p[N],c[N],f[N];
12 
13 template<typename T>void read(T &x) {
14     char ch=getchar(); T f=1; x=0;
15     while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
16     if(ch=='-') f=-1,ch=getchar();
17     for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
18 }
19 
20 #define pr pair<int,int>
21 #define fi first
22 #define se second
23 #define lc (x<<1)
24 #define rc ((x<<1)|1)
25 #define mid ((l+r)>>1)
26 #define mp make_pair
27 pr s[N<<2],sl[N<<2];
28 
29 pr operator +(const pr&A,const pr&B) {
30     if(A.fi==0) return B; if(B.fi==0) return A;
31     return mp(max(A.fi,B.fi),min(A.se,B.se));
32 }
33 
34 void build(int x,int l,int r) {
35     s[x]=sl[x]=mp(0,inf); //A1
36     if(l==r) return;
37     build(lc,l,mid); build(rc,mid+1,r);
38 }
39 
40 pr calc(int x,int l,int r,int last) {
41     if(l==r) { return s[x].fi>last?s[x]:mp(0,inf); }
42     if(s[rc].fi>last) return sl[x]+calc(rc,mid+1,r,last);
43     else return calc(lc,l,mid,last);
44 }
45 
46 void upd(int x,int l,int r) {
47     s[x]=s[lc]+s[rc];
48     sl[x]=calc(lc,l,mid,s[rc].fi);
49 }
50 
51 void update(int x,int l,int r,int pos,pr t) {
52     if(l==r) { s[x]=t; return ; }
53     if(pos<=mid) update(lc,l,mid,pos,t);
54     else update(rc,mid+1,r,pos,t);
55     upd(x,l,r);
56 }
57 
58 pr rs; //(0,inf)
59 void qry(int x,int l,int r,int ql,int qr) {
60     if(l>=ql&&r<=qr) { rs=rs+calc(x,l,r,rs.fi); return; }
61     if(qr>mid) qry(rc,mid+1,r,ql,qr);
62     if(ql<=mid) qry(lc,l,mid,ql,qr);
63 }
64 
65 #define ANS
66 int main() {
67 #ifdef ANS
68     freopen("knows.in","r",stdin);
69     freopen("knows.out","w",stdout);
70 #endif
71     read(n);
72     For(i,1,n) read(p[i]);
73     For(i,1,n) read(c[i]);
74     build(1,1,n);
75     For(i,1,n) {
76         rs=mp(0,inf);
77         qry(1,1,n,1,p[i]);
78         f[i]=rs.fi==0?c[i]:rs.se+c[i];
79         update(1,1,n,p[i],mp(i,f[i]));
80     }
81     rs=mp(0,inf);
82     qry(1,1,n,1,n);
83     printf("%d\n",rs.se);
84     Formylove;
85 }
86 /*
87 5
88 3 1 4 5 2
89 3 4 3 4 1
90 */
View Code

 

T3.Lost my music

代码十分短小优美,用大佬的写题解方式就是:这是一道可持久化栈维护凸包的裸题。

 

我记得这题是可持久化栈维护凸包,但是并打不来。

题目要求的是一个斜率形式,画到图上就是求前面的点到我的斜率的最小值,

如图,中间的j点无论如何都不优,所以维护的这样一个上凸壳

并且我和我在凸壳上的前一个点的斜率就是我的答案(我考场上竟然写了在凸包上三分,我到底在想些什么???)

凸壳是用单调栈维护的,这个在树上就得搞个可持久化栈了。可持久化栈用倍增实现,f[x][i]表示凸壳上我前面的2^i个点,弹栈的时候就可以倍增地弹,就相当于回溯到那个点所维护的凸壳的状态。

Rep(i,18,0) if(f[tx][i]&&f[f[tx][i]][0]&&dcmp(xl(f[tx][i],x)-xl(f[tx][i],f[f[tx][i]][0])>=0)) {
		tx=f[f[tx][i]][0];	
}
if(f[tx][0]&&dcmp(xl(tx,x)-xl(tx,f[tx][0])>=0)) tx=f[tx][0];

因为我弹的时候是从我前面的前面开始弹的,相当于没考虑只弹一个的情况,所以要有下面那句等于特判。

 1 //Achen
 2 #include<bits/stdc++.h>
 3 #define For(i,a,b) for(int i=(a);i<=(b);i++)
 4 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
 5 #define Formylove return 0
 6 const int N=5e5+7;
 7 using namespace std;
 8 typedef long long LL;
 9 typedef double db;
10 int n,c[N],f[N][20];
11 db ans[N];
12 
13 template<typename T>void read(T &x) {
14     char ch=getchar(); T f=1; x=0;
15     while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
16     if(ch=='-') f=-1,ch=getchar();
17     for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
18 }
19 
20 int ecnt,fir[N],nxt[N<<1],to[N<<1];
21 void add(int u,int v) {
22     nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
23 }
24 
25 int d[N];
26 #define eps 1e-10
27 int dcmp(db x) { return fabs(x)<eps?0:(x>0?1:-1); }
28 db xl(int a,int b) { 
29     return (-c[a]+c[b])/((db)d[a]-d[b]); 
30 }
31 
32 void DFS(int x,int p) {
33     d[x]=d[p]+1;
34     int tx=p;
35     Rep(i,18,0) if(f[tx][i]&&f[f[tx][i]][0]&&dcmp(xl(f[tx][i],x)-xl(f[tx][i],f[f[tx][i]][0])>=0)) {
36         tx=f[f[tx][i]][0];    
37     }
38     if(f[tx][0]&&dcmp(xl(tx,x)-xl(tx,f[tx][0])>=0)) tx=f[tx][0];
39     f[x][0]=tx;
40     if(x!=1) ans[x]=xl(tx,x);
41     For(i,1,18) f[x][i]=f[f[x][i-1]][i-1];
42     for(int i=fir[x];i;i=nxt[i]) 
43         DFS(to[i],x);
44 }
45 
46 #define ANS
47 int main() {
48 #ifdef ANS
49     freopen("lost.in","r",stdin);
50     freopen("lost.out","w",stdout);
51 #endif
52     read(n);
53     For(i,1,n) read(c[i]);
54     For(i,2,n) {
55         int fx;
56         read(fx);
57         add(fx,i);
58     }
59     DFS(1,0);
60     For(i,2,n) printf("%.10lf\n",ans[i]);
61     Formylove;
62 }
View Code

 

posted @ 2018-09-20 22:11  啊宸  阅读(210)  评论(0编辑  收藏  举报