zoukankan      html  css  js  c++  java
  • [考试反思]1014csp-s模拟测试73:侵蚀

    嗯。。。还是没有改变那个现状

    依旧只是打满了暴力,虽说T2打的的确比暴力好很多,但是因为出题人没有设分所以和暴力等同。

    离上面的分差还是大的很,下面还是追的很紧

    而且进几场的排名也是连续下滑。。。

    虽说比前一段时间上下弹跳好一些吧。。。但是这肯定是不够的啊

    还需要努力。还能进步。

    T1大模拟,细节打飞调样例调了好久,花了一个多小时。。。

    T2理论复杂度勉强但是实际自带巨大常数根本卡不过,我还以为我想到了正解

    “我天,二分答案套线段树优化动态规划,神仙题啊,我竟然做出来了,这次稳了”

    然后随便来一组随机数据,7s。。。我以为是常数的锅,卡了一阵到3s

    其实复杂度就是多了一个log。

    二分答案的上界是1e14,eps是1e-4,这样的话log掉也有60。。。

    更可怕的是,这样二分答案的值需要有1e-18的精度,double无法接受,死循环了。。。

    long double就T死。。。

    其实到这里就应该意识到这不是正解了。。。但是没时间了

    T3连忙打了一个部分分。

    还有4分钟的时候,想摸鱼了。

    但是吸取之前的exp,不要摸鱼不要摸鱼不要摸鱼,于是开始打状压

    码出来了!多了15分!

    一定不要浪费任何一分钟。

    一定不要怀疑自己的码力。

    计算复杂度时要考虑常数。

    T1:小P的2048

    模拟,还是不讲了吧。。。

    不要颓啊啊啊

     1 #include<cstdio>
     2 int n,m,f[9][9],p,d,v,rm,s,r[9][9];
     3 void put(){for(int i=1;i<=n;++i,puts(""))for(int j=1;j<=n;++j)printf("%d ",f[i][j]);puts("");}
     4 void up(){
     5     for(int j=1;j<=n;++j)for(int i=2;i<=n;++i)if(f[i][j]){
     6         int pos=i;while(pos>1&&!f[pos-1][j])pos--;
     7         if(pos!=i)f[pos][j]=f[i][j],f[i][j]=0;
     8     }
     9     for(int j=1;j<=n;++j)for(int i=2;i<=n;++i)if(f[i][j])if(f[i-1][j]==f[i][j])
    10         f[i-1][j]<<=1,f[i][j]=0,s+=f[i-1][j];
    11     for(int j=1;j<=n;++j)for(int i=2;i<=n;++i)if(f[i][j]){
    12         int pos=i;while(pos>1&&!f[pos-1][j])pos--;
    13         if(pos!=i)f[pos][j]=f[i][j],f[i][j]=0;
    14     }
    15 }
    16 void down(){
    17     for(int j=1;j<=n;++j)for(int i=n-1;i;--i)if(f[i][j]){
    18         int pos=i;while(pos<n&&!f[pos+1][j])pos++;
    19         if(pos!=i)f[pos][j]=f[i][j],f[i][j]=0;
    20     }
    21     for(int j=1;j<=n;++j)for(int i=n-1;i;--i)if(f[i][j])if(f[i+1][j]==f[i][j])
    22         f[i+1][j]<<=1,f[i][j]=0,s+=f[i+1][j];
    23     for(int j=1;j<=n;++j)for(int i=n-1;i;--i)if(f[i][j]){
    24         int pos=i;while(pos<n&&!f[pos+1][j])pos++;
    25         if(pos!=i)f[pos][j]=f[i][j],f[i][j]=0;
    26     }
    27 }
    28 void left(){
    29     for(int i=1;i<=n;++i)for(int j=2;j<=n;++j)if(f[i][j]){
    30         int pos=j;while(pos>1&&!f[i][pos-1])pos--;
    31         if(pos!=j)f[i][pos]=f[i][j],f[i][j]=0;
    32     }
    33     for(int i=1;i<=n;++i)for(int j=2;j<=n;++j)if(f[i][j])if(f[i][j-1]==f[i][j])
    34         f[i][j-1]<<=1,f[i][j]=0,s+=f[i][j-1];
    35     for(int i=1;i<=n;++i)for(int j=2;j<=n;++j)if(f[i][j]){
    36         int pos=j;while(pos>1&&!f[i][pos-1])pos--;
    37         if(pos!=j)f[i][pos]=f[i][j],f[i][j]=0;
    38     }
    39 }
    40 void right(){
    41     for(int i=1;i<=n;++i)for(int j=n-1;j;--j)if(f[i][j]){
    42         int pos=j;while(pos<n&&!f[i][pos+1])pos++;
    43         if(pos!=j)f[i][pos]=f[i][j],f[i][j]=0;
    44     }
    45     for(int i=1;i<=n;++i)for(int j=n-1;j;--j)if(f[i][j])if(f[i][j+1]==f[i][j])
    46         f[i][j+1]<<=1,f[i][j]=0,s+=f[i][j+1];
    47     for(int i=1;i<=n;++i)for(int j=n-1;j;--j)if(f[i][j]){
    48         int pos=j;while(pos<n&&!f[i][pos+1])pos++;
    49         if(pos!=j)f[i][pos]=f[i][j],f[i][j]=0;
    50     }
    51 }
    52 int main(){//freopen("game_sample2.in","r",stdin);
    53     scanf("%d%d",&n,&m);rm=m;
    54     for(int x,y,V,i=1;i<=2;++i)scanf("%d%d%d",&x,&y,&V),f[x][y]=V;
    55     for(int T=1;T<=m;++T){
    56         scanf("%d%d%d",&d,&p,&v);
    57         for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)r[i][j]=f[i][j];
    58         if(d==0)up();else if(d==1)down();else if(d==2)left();else right();
    59         for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)if(r[i][j]!=f[i][j])goto Y;
    60         rm=T-1;goto gg;
    61 Y:        int cnt=0;for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)if(!f[i][j])cnt++;
    62         p=p%cnt+1;//printf("%d
    ",cnt);
    63         for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)if(!f[i][j]){p--;if(!p){f[i][j]=v;goto ex;}}
    64 ex:;
    65     }
    66 gg: printf("%d
    %d
    ",rm,s);
    67 }
    View Code

    T2:小P的单调数列

    记一下考场上的思路,其实不错。

    如果你不知道那个结论的话,你要考虑怎么处理平均数。

    二分答案,设为x,需要check。

    即我们需要判断sum>kx是否成立,k表示段数。

    那么其实就是每多一段你的sum值就减少了x。

    设dp[i][0/1]表示考虑完目前序列的最后一个值为i,目前处于上升/下降段。

    转移比较显然,就是线段树优化dp的板子。

    复杂度$O(n log n log(10^9n/eps))$,理论复杂度可过,实际60分(同$O(n^2)$)

     1 //二分答案判平均值:所有区间结束后都减去二分值,最后大于0即可
     2 //dp[i][0/1]是第i位必选,目前是向上还是向下
     3 //离散化+线段树优化dp?
     4 #include<cstdio>
     5 #include<unordered_map>
     6 #include<algorithm>
     7 using namespace std;
     8 double max(double a,double b){return a>b?a:b;}
     9 unordered_map<int,int>M;
    10 int n,a[100005],re[100005],cnt;
    11 struct Segment_tree{
    12     int cl[800005],cr[800005];double w[800008];
    13     void build(int p,int l,int r,double sw){
    14         w[p]=sw;cl[p]=l;cr[p]=r;
    15         if(l==r)return;
    16         build(p<<1,l,l+r>>1,sw);build(p<<1|1,(l+r>>1)+1,r,sw);
    17     }
    18     void set(int p,int pos,double v){
    19         if(cl[p]==cr[p]){w[p]=max(v,w[p]);return;}
    20         if(pos<=cr[p<<1])set(p<<1,pos,v);
    21         else set(p<<1|1,pos,v);
    22         w[p]=max(w[p<<1],w[p<<1|1]);
    23     }
    24     double ask(int p,int l,int r){
    25         if(l>r)return -1e18;
    26         if(l<=cl[p]&&cr[p]<=r)return w[p];
    27         return max(l<=cr[p<<1]?ask(p<<1,l,r):-1e18,r>=cl[p<<1|1]?ask(p<<1|1,l,r):-1e18);
    28     }
    29 }up,down;
    30 bool chk(double x){
    31     up.build(1,0,cnt,0);down.build(1,0,cnt,-1e18);
    32     for(int i=1;i<=n;++i){
    33         double ux=up.ask(1,0,a[i]-1),ut=up.ask(1,0,a[i]),dx=down.ask(1,a[i]+1,cnt),dt=down.ask(1,a[i],cnt);
    34         up.set(1,a[i],max(ux,dt-x)+re[a[i]]);down.set(1,a[i],max(dx,ut-x)+re[a[i]]);
    35     }
    36     return max(up.ask(1,0,n),down.ask(1,0,n))>x;
    37 }
    38 int main(){
    39     scanf("%d",&n);
    40     for(int i=1;i<=n;++i)scanf("%d",&a[i]),re[i]=a[i];
    41     sort(re+1,re+1+n);
    42     for(int i=1;i<=n;++i)if(re[i]!=re[i-1])M[re[i]]=++cnt,re[cnt]=re[i];
    43     for(int i=1;i<=n;++i)a[i]=M[a[i]];
    44     double l=0,r=1e14;
    45     while(r-l>1e-5)if(chk((l+r)/2))l=(l+r)/2;else r=(l+r)/2;
    46     printf("%.3lf
    ",l);
    47 }
    可恶的出题人一分不多给

    正解需要一个结论:最优解是一个上升段或一个升段+一个降段

    证明不是很难,分析平均数的加和性的结果即可。

    那么就是简单dp,枚举最高点,两边跑最长上升子序列,树状数组维护,$O(n log n)$。

     1 #include<cstdio>
     2 #include<unordered_map>
     3 #include<algorithm>
     4 using namespace std;
     5 #define int long long
     6 unordered_map<int,int>M;
     7 int max(int a,int b){return a>b?a:b;}
     8 int n,a[100005],re[100005],cnt,dp[100005],t[100005],DP[100005],fdp[100005],bdp[100005],ans;
     9 void set(int p,int w){for(;p<=100000;p+=p&-p)t[p]=max(t[p],w);}
    10 int ask(int p,int a=0){for(;p;p^=p&-p)a=max(a,t[p]);return a;}
    11 main(){//freopen("2.in","r",stdin);
    12     scanf("%lld",&n);
    13     for(int i=1;i<=n;++i)scanf("%lld",&a[i]),re[i]=a[i];
    14     sort(re+1,re+1+n);
    15     for(int i=1;i<=n;++i)if(re[i]!=re[i-1])M[re[i]]=++cnt,re[cnt]=re[i];
    16     for(int i=1;i<=n;++i)a[i]=M[a[i]];
    17     for(int i=1;i<=n;++i)dp[a[i]]=ask(a[i]-1)+re[a[i]],set(a[i],dp[a[i]]),fdp[i]=dp[a[i]],ans=max(ans,dp[a[i]]<<1);
    18     for(int i=0;i<=cnt;++i)t[i]=0;
    19     for(int i=n;i;--i)DP[a[i]]=ask(a[i]-1)+re[a[i]],set(a[i],DP[a[i]]),bdp[i]=DP[a[i]];
    20     for(int i=1;i<=n;++i)ans=max(ans,fdp[i]+bdp[i]-re[a[i]]);//,printf("%lld %lld %lld %lld
    ",i,fdp[i],bdp[i],fdp[i]+bdp[i]-re[a[i]]);
    21     printf("%.3lf
    ",ans/2.0);
    22 }
    View Code

    T3:小P的生成树

    考虑最大生成树的过程,关于树的边集,其实你在意的不是具体长度,而是相对关系。

    显然,在某一角度下的模长和等于向量先求和后求模长。

    这样有一个想法就是枚举这个角度。可以AC。

     1 #include<cstdio>
     2 #include<cmath>
     3 #include<algorithm>
     4 #include<iostream>
     5 using namespace std;
     6 struct es{
     7     int x,y,a,b;double mod;
     8     friend bool operator<(es a,es b){return a.mod>b.mod;}
     9 }E[205];
    10 int n,f[55],m;double ans;
    11 int find(int k){return k==f[k]?k:f[k]=find(f[k]);}
    12 int main(){
    13     scanf("%d%d",&n,&m);
    14     for(int i=1;i<=m;++i)scanf("%d%d%d%d",&E[i].x,&E[i].y,&E[i].a,&E[i].b);
    15     for(double alpha=0;alpha<3.1415926*2;alpha+=0.11){
    16         double sine=sin(alpha),cosine=cos(alpha);long long totx=0,toty=0;
    17         for(int i=1;i<=m;++i)E[i].mod=E[i].a*cosine+E[i].b*sine;
    18         sort(E+1,E+1+m);
    19         for(int i=1;i<=n;++i)f[i]=i;
    20         for(int i=1;i<=m;++i)if(find(E[i].x)!=find(E[i].y))
    21             totx+=E[i].a,toty+=E[i].b,f[f[E[i].x]]=f[E[i].y];
    22         ans=max(sqrt(totx*totx+toty*toty),ans);
    23     }printf("%.6lf
    ",ans);
    24 }
    81ms

    正解更加严谨。

    既然只有相对大小关系有影响,那么你考虑任意两条边,它们的大小关系改变时有一个确切的角度。

    m2枚举出所有这样的角度,将坐标系划分为m2部分,在每一部分里最大生成树都一样。

    在这m2部分里各任意取最点更新最佳答案即可。

     1 #include<cstdio>
     2 #include<cmath>
     3 #include<algorithm>
     4 #include<iostream>
     5 using namespace std;
     6 struct es{
     7     int x,y,a,b;double mod;
     8     friend bool operator<(es a,es b){return a.mod>b.mod;}
     9 }E[205];
    10 int n,f[55],m;double ans;
    11 int find(int k){return k==f[k]?k:f[k]=find(f[k]);}
    12 int main(){
    13     scanf("%d%d",&n,&m);
    14     for(int i=1;i<=m;++i)scanf("%d%d%d%d",&E[i].x,&E[i].y,&E[i].a,&E[i].b);
    15     for(int i=1;i<=m;++i)for(int j=i+1;j<=m;++j){
    16         double alpha=atan(1.0*(E[i].y-E[j].y)/(E[i].x-E[j].x))+0.00001;bg:
    17         double sine=sin(alpha),cosine=cos(alpha);long long totx=0,toty=0;
    18         for(int i=1;i<=m;++i)E[i].mod=E[i].a*cosine+E[i].b*sine;
    19         sort(E+1,E+1+m);
    20         for(int i=1;i<=n;++i)f[i]=i;
    21         for(int i=1,al=0;i<=m,al<n-1;++i)if(find(E[i].x)!=find(E[i].y))
    22             totx+=E[i].a,toty+=E[i].b,f[f[E[i].x]]=f[E[i].y],al++;
    23         ans=max(sqrt(totx*totx+toty*toty),ans);
    24         if(alpha<3.1415926){alpha+=3.1415926;goto bg;}
    25     }printf("%.6lf
    ",ans);
    26 }
    4300ms
  • 相关阅读:
    python基本数据类型剖析
    常用正则表达式
    python_re模块
    迭代器模式
    状态模式
    备忘录模式
    asp.net 发送邮件
    建造者模式
    抽象工厂模式
    摸板模式与钩子
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11674168.html
Copyright © 2011-2022 走看看