17.7.26离线赛比赛总结

A

结果和评价

  • 得分 70 基本分。
  • 时间 30 + 60

我的思路和方向

  • 70 很容易。之后就在想怎么优化这个 O(n4) DP 可是一直在想如果把一个点的答案贡献到其他的点,想到了许多没有什么用的东西。。。例如差分前缀和之类的。最终我们想出来还浪费的大量的时间。

思路更正

反思

  • 思考方向上有两个致命的错误:
    • Dp 转移方向太过单一,我可能是思维定型了。。一直在想办法优化前面这种。
      • 可以把每个点的贡献赋给其他点
      • 可以在每个点去搜集对它有贡献点的
    • 同时,面对怎么多的维度我竟然没有想到通过一些方式消维
  • 小结
    • 分析问题的维度,即问题的限制条件。
    • Dp 的转移优化时,也要一个一个维度的分析,同时分析的过程要先模拟清楚,比较 Dp 的代码量并不大。

B

结果和评价

  • 得分 20
  • 时间 20 + 40
  • 评价:™智障。。
  • 少打一个等于号打成了if(n<10000)P60.work()sum=3)*********

我的思路和方向

  • 很容易切到 60 分。结果。。
  • 之后正解想不出来了。。

思路更正

分析题目的限制条件
  • 我们需要求 A[i] (和 x 平行的线)和B[j](和 y 平行的线)的交点。
  • 同时我们还要判掉不合法的点。

深入思考

  • 为了避免每个线段交点数量统计错误,我们可以分别处理B A 的贡献,再处理A B 的贡献

    • 我们发现如果我们要判掉一个A[i]是否和 B[j] 相交有两个维度。

      • 例如这样一个情况

        .............................. 
        ...........444444444414444....
        ..22222222...........1........
        .....................1........
        .....................1........
        .....................1........
        ..............................
        ..........3333333333333.......
        ..............................
      • 此时我们发现和 1 相交的只有4,因为 2 x不合法, 3 y不合法,这样我们就不好优化复杂度了。
    • 如何消维?我们发现对于 y 这一维,如果所有的A它的长度都为无限大就好了,于是我们可以直接忽略这一维了。这样对 B 排序就可以以O(n)的复杂度,求出每一个 A B[...]相交的个数了,总的复杂度还是 O(logn)

    • 此时我们发现每个 B[...] 都有一个 y 会交到这条无限长的直线上。这样我们可以用Bit记录一个关于交点 y 值的前缀和。即:ans[x]=val[R]val[L1]
    • 于是我们在,扫描的时候只要把这个 y Bit里加入或者减去即可。
    • 这里有一个运用差分思想
      • 我们可以把 B L,R提取出两个数组后分别排序。同差分前缀和类似。
      • 对于比当前 A[i].x 小的所有 L 我们把他的y Bit 里加入。
      • 对于比当前 A[i].x 小的所有 R 我们把他的y Bit 里减去。
      • 于是我们就得到当前 ans[x]=val[a]val[b1]
    • 至于,如何判特殊的点呢?直接用 map 判就可以了。
    • struct node{int x,a,b,i;}A[M],B[M],C[M];
      bool cmx(node a,node b){return a.x<b.x;}
      bool cma(node a,node b){return a.a<b.a;}
      bool cmb(node a,node b){return a.b<b.b;}
      
      void calc(int n,int m){
          Sum.clear();
          sort(A+1,A+1+n,cmx);
          sort(B+1,B+1+m,cma);
          For(i,1,m)C[i]=B[i];
          sort(C+1,C+1+m,cmb);
          int pos1=1,pos2=1;
          For(i,1,n){
              int x=A[i].x;
              while(B[pos1].a<=x&&pos1<=m)Sum.add(B[pos1++].x,1); 
              while(C[pos2].b<x&&pos2<=m)Sum.add(C[pos2++].x,-1);
              ans[A[i].i]+=Sum.sum(A[i].a,A[i].b);
          }
      }
      int main(){
          int a,b,c,d,na=0,nb=0;
          rd(n);
      
          For(i,1,n){
              rd(a);rd(b);rd(c);rd(d);
              if(b>d)swap(b,d);if(a>c)swap(a,c);
              if(a==c)A[++na]=(node){a,b,d,i};
              else B[++nb]=(node){b,a,c,i};
          }
      
          calc(na,nb);swap(A,B);calc(nb,na);
      
          For(i,1,na){
              mark[mp(A[i].x,A[i].a)]=A[i].i;
              mark[mp(A[i].x,A[i].b)]=A[i].i;
          }
          For(i,1,nb){
              int x=mark[mp(B[i].a,B[i].x)];
              if(x)--ans[B[i].i],--ans[x];
              x=mark[mp(B[i].b,B[i].x)];
              if(x)--ans[B[i].i],--ans[x];
          }
          ll res=0;For(i,1,n)res+=ans[i];ptn(res/2);
          For(i,1,n)pt(ans[i]);
          return 0;
      }

      反思

      • 我也想过把每个 B 先用差分映射从一个平面,在用A扫一下,却认为 cnt 不好记录而否定了这个想法。™智障。。扫两次不就可以了吗。。
      • 还有。。上交代码的时候一定要在本地把所有的切分和切分的范围搞清楚了。在本地多出几个样例运行一下。。。

      C

      结果和评价

      • 得分爆0
      • 时间 10 + 40
      • 评价:™智障。。 把切分代码注释了(sum=4)*********

      我的思路和方向

      • 秒写30分。
      • 然后就不会,没有思路,想到了没有用的数位 Dp

      正确思路

      • 第一反应 ans=val[R]val[l1]
      • 通过 60 分的数据,我们可以直接枚举有几个数因 i 成为不吉利数字的。

      分析题目的限制条件和提示

      • longlong范围的 LR
      • a[i] 很恶心,不可以 Dp
      • 不知道怎么搜索,没有方向
      • 但是,我们发现我们可以枚举或者计算出因 i 成为不吉利数字的数的个数。

      深入分析

      • 不可能枚举每个数字了,也不可以分析数位了,我们应当找到可以枚举的东西,可以搜索的东西。
      • 没错,我们可以枚举每个每个不吉利数成为不吉利数的原因。即枚举i然后容斥,一共有 1024 个状态,比较小。
      • 可是 Dp 不能用了,我们该怎么办。。
      • 如果,没有上界的限制(告诉你 len a[i] )我们还是很容易用排列组合得到答案的。
      • 于是我们可以枚举从上界的哪一位开始搜索。
      • 最后我们处理前导0。
      ll dfs(int i,int m,int use=0,ll prod=1){
          if(i==10&&m>=0){
              /*如果[0,9]都扫完了此时至少有use个不吉利的数字,其他的随便填即可*/ 
              prod*=B[10-use][m];/*B[i][j]:从i种数种取出j个数组成不同的序列*/
              return ((use&1)?-1:1)*prod;
          }
          ll res=0;
          res+=dfs(i+1,m,use,prod);
          /*如果要让i成为不吉利的数字,从剩余的长度中取出a[i]个来即可*/ 
          if(a[i]<=m&&a[i]>=0)res+=dfs(i+1,m-a[i],use+1,prod*C[m][a[i]]);
          return res;
      }
      ll calc(ll x){
      
          int len=0;ll res=0;
          x++;/*我们这里求到的是严格小于x的答案不包含等于*/
      
          while(x)num[++len]=x%10,x/=10;
      
          For(i,0,9)a[i]=c[i];
      
          For(i,1,len/2)swap(num[i],num[len-i+1]);
          /*我们先枚举出有前导0的情况*/ 
          For(c,1,len-1){ 
              /*枚举有几个前导0,即我们只搜索后面[c+1,len]位,即长度小于等于len的段的答案*/ 
              res+=dfs(0,len-c);
              /*可是我们发现[c+1,len]包含了[c+2,len]的答案,我们强行给它加一个0把包含部分减掉*/
              a[0]--;
              res-=dfs(0,len-c-1);
              a[0]++;
              /*于是我们得到的答案即为长度只为len-c的段的答案*/
          }
          /*再枚举比x小的数时,我们枚举前几位*/
          For(i,1,len){
              /*要比本位小了,后面的数就可以乱取,于是我们可以用排列来算了*/ 
              For(c,i==1,num[i]-1){
                  a[c]--;
                  res+=dfs(0,len-i);
                  a[c]++;
              }
              a[num[i]]--;/*和原本的一样*/
          }
          return res;
      }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值