17.7.26离线赛比赛总结

来源:互联网 发布:男士皮鞋品牌 知乎 编辑:程序博客网 时间:2024/06/04 00:24

A

结果和评价

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

我的思路和方向

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

思路更正

  • 见CDQ专题。

反思

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

B

结果和评价

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

我的思路和方向

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

思路更正

分析题目的限制条件

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

深入思考

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

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

      • 例如这样一个情况

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

    • 此时我们发现每个B[...]都有一个y会交到这条无限长的直线上。这样我们可以用Bit记录一个关于交点y值的前缀和。即:ans[x]=val[R]val[L1]
    • 于是我们在,扫描的时候只要把这个yBit里加入或者减去即可。
    • 这里有一个运用差分思想
      • 我们可以把BL,R提取出两个数组后分别排序。同差分前缀和类似。
      • 对于比当前A[i].x小的所有L我们把他的yBit里加入。
      • 对于比当前A[i].x小的所有R我们把他的yBit里减去。
      • 于是我们就得到当前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不能用了,我们该怎么办。。
  • 如果,没有上界的限制(告诉你lena[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;}
原创粉丝点击