[考试反思]1015csp-s模拟测试75:混乱

赶上一套极其傻逼的题(是傻逼,不是简单)

T1超级卡精

T2模拟(输出卡"0.0"与"-0.0"不开spj),而且数据诡异乱打就能A(貌似给这道题的时间越长分越低)。

(B哥错解还一直嘲讽我和脸的正解,在随便一组手撸数据就hack他之前还一直说自己是正解,看NC多有素质)

T3极其麻烦的构造和题意转化

考试策略出锅了

昨天看到T2是模拟题极其开心。然后直接开始码。

码了一个小时,发现自己需要一个瓜丝消元,然而联想到了自己原来疯狂炸精的经历。

而且这道题的瓜丝还有自由元,怎么做来着不知道啊!!!

但是已经一个小时了总不能就这么扔了吧,于是开始yy瓜丝。

yy错了,过不了最大的样例,手撸了一个小的,又调了一个小时。

到这时,我一想,两个小时一个模拟,完戏。

于是去打T1,显然凸包啊,随手一打,发现卡精。

调来调去,手写分数,手写高精,十几个重载运算符,结果还不如不打。

又调了一会,忽然发现没多少时间了,T3还没动。

然后剩5分钟左右,状压是打不完了,博信仰拿10分。

注意时间。

在打出来之前一定要先想一想。

平时要学习适用性更强的板子。

 

T1:导弹袭击

这道题其实不是我不想好好写题解,只不过是因为出题人太毒瘤卡精让我很气愤。

凸包很好啊。就是判栈顶是不是比当前线差,目前的线和栈里第二个元素能不能让栈顶完全派不上用场。

算是单调栈维护凸包的裸题,但是不要求出斜率,用int存下ab,计算交点时用ab直接算。

注意处理平行线。

还是过不去的话用eps=7e-16卡一下。kx二分试出来的。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 struct line{
 4     int id,a,b;
 5     friend bool operator<(line x,line y){return x.a!=y.a?x.a<y.a:x.b<y.b;}
 6 }L[300005];
 7 map<pair<int,int>,int>m;vector<int>v[300005];
 8 int n,s[300005],tp,ok[300005];
 9 long double meet(line a,line b){return 1.0L*a.a*b.a*(a.b-b.b)/a.b/b.b/(b.a-a.a);}
10 int main(){//freopen("slay16.in","r",stdin);freopen("my.out","w",stdout);
11     scanf("%d",&n);
12     for(int i=1,a,b;i<=n;++i)scanf("%d%d",&L[i].a,&L[i].b),L[i].id=i;
13     sort(L+1,L+1+n);
14     for(int i=1;i<=n;++i){
15         if(m.find(make_pair(L[i].a,L[i].b))==m.end())m[make_pair(L[i].a,L[i].b)]=L[i].id;
16         v[m[make_pair(L[i].a,L[i].b)]].push_back(L[i].id);
17     }
18     for(int i=1;i<=n;++i){re:
19         if(m[make_pair(L[i].a,L[i].b)]!=L[i].id)continue;
20         while(tp&&L[s[tp]].a==L[i].a&&L[i].b>L[s[tp]].b)tp--;
21         if(tp>1&&meet(L[i],L[s[tp]])<meet(L[i],L[s[tp-1]])){tp--;goto re;}
22         if(tp&&meet(L[i],L[s[tp]])<7e-14){tp--;goto re;}
23         s[++tp]=i;
24     }
25     for(int i=1;i<=tp;++i)for(int j=0;j<v[L[s[i]].id].size();++j)ok[v[L[s[i]].id][j]]=1;
26     for(int i=1;i<=n;++i)if(ok[i])printf("%d ",i);puts("");
27 }
View Code

 

T2:炼金术士的疑惑

乱搞错解都能A还写什么题解

模拟。瓜丝消元。

设最后的?是k。

然后用最后一个式子求出任意一个变量关于k的表达式,带入已知式子就得到了关于k的方程组。

题目保证k有解。消元就是了。

和题解的正解不一样,但是skyh hack掉大部分人的那组数据hack不掉我,那就假装它是正解吧。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,cnt,sp,tp;
 4 map<string,int>mono;
 5 string s;
 6 double X[205][205],x,ans[205],spx;
 7 void debug(){return;for(int i=1;i<=n;++i,puts(""))for(int j=0;j<=cnt;++j)printf("%.1lf ",X[i][j]);puts("---");}
 8 double read(string S){
 9     int l=S.length(),ptr=-1;double num=0;
10     for(int i=0;i<l;++i)if(S[i]=='.')ptr=i;
11     if(ptr){for(int i=0;i<ptr;++i)num=num*10+S[i]-48;return num+(S[ptr+1]-48)/10.0;}
12     for(int i=0;i<l;++i)num=num*10+S[i]-48;return num;
13 }
14 void Gauss(){int ald=1;
15     for(int i=1;i<=n;++i){bg:ald++;if(ald>cnt)break;
16         int chl=i;double mxf=fabs(X[i][ald]);
17         for(int j=i;j<=n;++j)if(fabs(X[j][ald])>mxf)mxf=fabs(X[j][ald]),chl=j;
18         if(mxf<1e-4)goto bg;
19         for(int j=0;j<=cnt;++j)swap(X[i][j],X[chl][j]);
20         for(int j=i+1;j<=n;++j){
21             double rate=X[j][ald]/X[i][ald];
22             for(int k=0;k<=cnt;++k)X[j][k]-=rate*X[i][k];
23         }
24     }debug();
25     for(int i=n;i;--i){int fp=0;//printf("%d\n",i);
26         if(fabs(X[i][sp])>1e-4){spx*=X[i][0]/X[i][sp];return;}
27         for(int j=1;j<=cnt;++j)if(fabs(X[i][j])>1e-4)fp=j;
28         if(!fp)continue;
29         for(int j=i-1;j;--j){
30             double rate=X[j][fp]/X[i][fp];if(fabs(X[i][fp])<1e-4)continue;
31             for(int k=0;k<=cnt;++k)X[j][k]-=rate*X[i][k];
32         }debug();
33     }
34 }
35 int main(){//freopen("knight3.in","r",stdin);
36     cin>>n;
37     for(int i=1;i<=n;++i){
38         while(s!="="){
39             cin>>x>>s;
40             if(mono.find(s)==mono.end())mono[s]=++cnt;
41             X[i][mono[s]]=x;
42             cin>>s;
43         }
44         while(s!="H="){
45             cin>>x>>s;
46             if(mono.find(s)==mono.end())mono[s]=++cnt;
47             X[i][mono[s]]=-x;
48             cin>>s;
49         }
50         cin>>X[i][0];
51     }debug();
52     cin>>spx>>s;sp=mono[s];cin>>s;
53     while(s!="="){
54         cin>>x>>s;tp=mono[s];
55         for(int i=1;i<=n;++i)X[i][tp]-=X[i][sp]*x/spx;
56         cin>>s;
57     }
58     while(s!="H="){
59         cin>>x>>s;tp=mono[s];
60         for(int i=1;i<=n;++i)X[i][tp]+=X[i][sp]*x/spx;
61         cin>>s;
62     }debug();
63     Gauss();printf("%.1lf\n",spx+0.02);
64 }
View Code

附一组hack/调试用数据:

2

1 A + 1 B = 1 C H= 15

1 D + 1 E = 2 F H= 66

1 A + 1 B + 4 F = 1 C + 2 D + 2 E H= ?

 

T3:老司机的狂欢

题目名真是优雅。

但其实这道题还不错。和前两题比较起来。

题意转化有点复杂。

答案满足单调性,短时间会撞那么长时间撞的就更多了。。。

题目的提示也很明显,二分的上界都告诉你了:86400

你需要知道的是在某一个时间下最多能有几个司机。

不能相遇追及,那么也就是行走的路线不能有交集。

一个合法的选择方法,按照起点排序后,终点也是递增的。否则就相遇了。

LIS问题。离散化+树状数组做个dp。

这样再加上第二问输出-1就有60分了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 unordered_map<long long,int>M;
 4 struct driver{
 5     long long x,a;
 6     friend bool operator<(driver A,driver B){
 7         return A.x<B.x;
 8     }
 9 }d[100005];
10 long long cal(driver A,int tim){
11     return A.x*2+A.a*tim*tim;
12 }
13 int n,t[100005];long long x2[100005];
14 void modify(int p,int w){for(;p<=n;p+=p&-p)t[p]=max(t[p],w);}
15 int ask(int p,int w=0){for(;p;p^=p&-p)w=max(w,t[p]);return w;}
16 int chk(int tim){int ans=0;
17     for(int i=1;i<=n;++i)x2[i]=cal(d[i],tim);
18     sort(x2+1,x2+1+n);
19     for(int i=1;i<=n;++i)t[i]=0;
20     M.clear();int cnt=0;
21     for(int i=1;i<=n;++i)if(i==1||x2[i]!=x2[i-1])M[x2[i]]=++cnt;
22     for(int i=1;i<=n;++i)x2[i]=M[cal(d[i],tim)],ans=max(ans,ask(x2[i]-1)+1),modify(x2[i],ask(x2[i]-1)+1);//printf("%d %d\n",tim,ans);
23     return ans;
24 }
25 int main(){int k;
26     scanf("%d%d",&n,&k);
27     for(int i=1;i<=n;++i)scanf("%lld%lld",&d[i].x,&d[i].a);
28     sort(d+1,d+1+n);
29     int l=0,r=86400;
30     while(l<r-1)if(chk(l+r>>1)>=k)l=l+r>>1;else r=l+r>>1;
31     if(chk(r)>=k)l=r;
32     printf("%d\n-1\n",l);
33 }
到这里代码还没有到1k

怎么构造最小的字典序?

考虑每一种方案,如果你记录上你每一个点的最优决策是从哪里转移而来的,那么每个点都只有一个父亲。

就是一个树形结构。

然后加入你现在要转移了,每个点上的dp不再是一个int值而是一个二元组。

first是深度(即原dp值),second是这个点的位置(按起点排序后)。

然后和原来差不多,我们要用一个树状数组,不过这次要维护二元组之间的大小。

根据dp转移的定义,如果first不同,first更大的优。

否则就要比较字典序了,这时候你已经确定了它们的深度相同。

我们维护这条链上所有老司机的编号值,要求比较哪一个排序后的字典序最小。

对于i和j,设其lca为l,那么在l及以上的部分两条链是完全一样的,所以不需要比较。

我们只需要比较(l,i)和(l,j)的关系。

而因为每个点都在树里只出现了一遍,所以在lca以下的部分中,两条链上没有老司机编号相同的点。

那么要比较字典序的话,其实就是在比较两条链在lca以下的最小值谁更小。

最小值较小者排完序之后的字典序就更小。这些用树上倍增搞就可以了。

然后就可以用这种比较法则去更新树状数组和最终的答案以及维护树状数组了。

最后只要不断在决策树上跳父亲即可。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int min(int a,int b){return a<b?a:b;}
 4 unordered_map<long long,int>M;
 5 struct driver{
 6     long long x,a;int id;
 7     friend bool operator<(driver A,driver B){
 8         return A.x<B.x;
 9     }
10 }d[100005];
11 long long cal(driver A,int tim){
12     return A.x*2+A.a*tim*tim;
13 }
14 int n,t[100005];long long x2[100005];
15 void modify(int p,int w){for(;p<=n;p+=p&-p)t[p]=max(t[p],w);}
16 int ask(int p,int w=0){for(;p;p^=p&-p)w=max(w,t[p]);return w;}
17 int chk(int tim){int ans=0;
18     for(int i=1;i<=n;++i)x2[i]=cal(d[i],tim);
19     sort(x2+1,x2+1+n);
20     for(int i=1;i<=n;++i)t[i]=0;
21     M.clear();int cnt=0;
22     for(int i=1;i<=n;++i)if(i==1||x2[i]!=x2[i-1])M[x2[i]]=++cnt;
23     for(int i=1;i<=n;++i)x2[i]=M[cal(d[i],tim)],ans=max(ans,ask(x2[i]-1)+1),modify(x2[i],ask(x2[i]-1)+1);
24     return ans;
25 }
26 int f[100005][17],dep[100005],mn[100005][17],ok[100005];
27 int lca(int a,int b){
28     for(int i=16;~i;--i)if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i];
29     return f[a][0];
30 }
31 int get_val(int p,int anc){
32     int sub=dep[p]-dep[anc],ans=1000000000;
33     for(int i=16;~i;--i)if(sub&1<<i)ans=min(ans,mn[p][i]),p=f[p][i];
34     return ans;
35 }
36 bool com(pair<int,int>a,pair<int,int>b){
37     if(a.first!=b.first)return a.first>b.first;
38     int LCA=lca(a.second,b.second);
39     return get_val(a.second,LCA)<get_val(b.second,LCA);
40 }
41 pair<int,int>T[100005];
42 void modify(int p,pair<int,int>w){for(;p<=n;p+=p&-p)if(com(w,T[p]))T[p]=w;}
43 pair<int,int> query(int p,pair<int,int>w=make_pair(0,0)){for(;p;p^=p&-p)if(com(T[p],w))w=T[p];return w;}
44 pair<int,int> get_plan(int tim){pair<int,int>ans=make_pair(0,0);
45     for(int i=1;i<=n;++i)x2[i]=cal(d[i],tim);
46     sort(x2+1,x2+1+n);
47     for(int i=1;i<=n;++i)T[i]=make_pair(0,0);
48     M.clear();int cnt=0;
49     for(int i=1;i<=n;++i)if(i==1||x2[i]!=x2[i-1])M[x2[i]]=++cnt;
50     for(int i=1;i<=n;++i){
51         x2[i]=M[cal(d[i],tim)];
52         pair<int,int>tmp=query(x2[i]-1);tmp.first++;
53         f[i][0]=tmp.second;mn[i][0]=d[i].id;dep[i]=tmp.first;
54         for(int j=1;j<=16;++j)f[i][j]=f[f[i][j-1]][j-1],mn[i][j]=min(mn[i][j-1],mn[f[i][j-1]][j-1]);
55         tmp.second=i;
56         modify(x2[i],tmp);if(com(tmp,ans))ans=tmp;
57     }return ans;
58 }
59 int main(){int k;
60     scanf("%d%d",&n,&k);
61     for(int i=1;i<=n;++i)scanf("%lld%lld",&d[i].x,&d[i].a),d[i].id=i;
62     sort(d+1,d+1+n);
63     int l=0,r=86400;
64     while(l<r-1)if(chk(l+r>>1)>=k)l=l+r>>1;else r=l+r>>1;
65     if(chk(r)>=k)l=r;printf("%d\n",l);
66     if(chk(l)>k){puts("-1");return 0;}
67     int g=get_plan(l).second;
68     while(g)ok[d[g].id]=1,g=f[g][0];
69     for(int i=1;i<=n;++i)if(ok[i])printf("%d ",i);puts("");
70 }
然后它就2.4k了

思路积累:

  • 把最优dp转移的过程抽象为树形来进行优化
posted @ 2019-10-16 12:10  DeepinC  阅读(279)  评论(2编辑  收藏  举报