• NOI 2018网络同步赛(游记?)


      刚中考完那段时间比较无聊,报名了一个同步赛,报完名才发现成绩单是要挂到网上的,而且因为报的早给了一个很靠前的考号...那布星啊,赶紧学点东西,于是在一周内学了网络流,Treap以及一些数论。

      Day1:

      作为一个全国的信息学组织,官网竟然卡成这样,按理说OI选手应该学做网站也会简单一点吧...无法接受.jpg。终于还是在9点前拿到了题。浏览一遍题面感觉只有第一题比较可做,T2T3的暴力也不是很难打?

      归程:https://www.luogu.org/problemnew/show/P4768

      看到离线完全没有在意,写了一个最短路+BFS,50.本来还有10分可以写树剖,然而觉得写起来比较麻烦以至于没写。事实上离线的做法非常简单,最短路+并查集;关键是这个做法很有启发性,如果会写离线做法就离A题不远了。

      T2写了next_permutation以及归并排序,期望得分8分,然而交错了题于是爆零。

      T3写了4分的暴力,没有去重于是爆零。

      于是Day1得分:50+0+0=50;

      听有人说Cu线40,真是难以置信。


      Day2:

      因为Day1大爆炸,于是对Day2也没有什么希望。开题:XXX,XXX,多边形。emmmm多边形?计算几何吗?不过本来也没有打算做Day2T3于是愉快的开始看题啦。

      第一眼感觉T1是个dp,T2网络流,T3...不知道是个什么...

      仔细看T1,发现了一些奥妙重重的东西。“按照1-n的顺序”,顺序是固定的啊。“选择XXX的剑”,选剑也是固定的?必须一次把龙打死,不可以打好几个回合。那就不是dp了,好像没有什么决策之类的东西。使得x最小,二分!似乎并不满足单调性。

      $$(a_i-atk_i*x)\%p_i=0$$

      写出这个式子后仿佛发现了什么...看数据范围,“保证$p_i$是质数”,“lcm”,果然是个数论题目啊,感觉非常可做!

      首先找一个set维护选哪些剑,然而set的各种操作非常不熟悉,调了好久决定手写一个Treap,此时大约10:00,还有三个半小时,难道连半道题都做不出来吗?

      $$atk_i*x=a_i(\%p_i)$$

      写了一个excrt(从网上抄的)解方程,然而似乎并不能解带系数的方程,难道是我把这道题想简单了吗?后来想到把$akt_i$的逆元乘过去就是普通的excrt啦。然而...P不是质数怎么办呢?一开始以为是无解,后来发现似乎并不是这样。可以把式子重新展开:

      $$atk_i*x-a_i=p_i*y$$

      可以把这里面的公因数全部除掉。看到这里你可能会觉得非常奇怪,因为除掉公因数的$p_i$有可能还是不是质数啊?逆元有两种求法,快速幂的方法肯定是不行了,但是扩欧还可以抢救一下。

      Q:如果扩欧还是求不出逆元呢?A:这样就真的无解了。

      再套用excrt,这道题就做完了。真的完成了吗,其实还没有。

      这里面有几处乘法是爆longlong的,所以要用快速乘,另外也要注意只要是取模尽量往前提,和乘法合并在一起,比如说赛后改了一个小时的这个地方:

      1 x=(a[i]-A)/d*x;
     2 t=p[i]/d;  3 x=(x%t+t)%t;

      如果这样写就会爆longlong,如果把第三行提到第一行边乘边取模就可以了。

      顺便注意快速乘里面的左移应该写成这样:$a<<1LL$,每个常数都强转成longlong才保险。

      其实还没有结束呢。对于任意的龙,我们还需要满足一个条件:$atk_i*x>=a_i$,不过这个地方还是比较简单的,因为这是个方程组,套用crt求最小整数的思想,任意解加减所有模数的最小公倍数后还是一个解,所以预处理一下把每条龙砍成非正数血的最小次数后取max,如果最终的答案小于这个值,就不停的加最小公倍数直到满足条件。

      看起来好像不太难?然而我考试时提交的版本仅有35分,如果数组开到足够大可以有45分。这个AC思路我写了整整一天。也许,这个题对于NOI选手是个签到题?

      
      1 // luogu-judger-enable-o2
      2 # include <cstdio>
      3 # include <iostream>
      4 # include <cstdlib>
      5 # define LL long long
      6 # define inf (long long)(1e9)
      7 
      8 inline LL min (LL x,LL y) {    if(x>y) return y; return x; }
      9 inline LL max (LL x,LL y) {    if(x>y) return x; return y; }
     10 int T,n,m,num;
     11 const int maxn=1000005;
     12 LL a[maxn],lcmm,minc,m1,m2,c1,c2;
     13 LL p[maxn],ans; 
     14 LL g[maxn],x,exx,exy;
     15 LL atk[maxn];
     16 
     17 LL mu(LL a,LL b,LL p)
     18 {
     19     a=(a%p+p)%p;
     20     b=(b%p+p)%p;
     21     LL res=0;
     22     while(b)
     23     {
     24         if(b&1LL) res=(res+a)%p;
     25         a=(a+a)%p;
     26         b>>=1LL;
     27     }
     28     return res;
     29 }
     30 
     31 struct node
     32 {
     33     node *ch[2];
     34     LL v;
     35     int r,s,n;
     36     int cmp(LL x)
     37     {
     38         if(x==v) return -1;
     39         if(x>v) return 1;
     40         return 0;
     41     }
     42     void in(LL x)
     43     {
     44         v=x;
     45         r=rand();
     46         s=n=1;
     47         ch[0]=ch[1]=NULL;
     48     }
     49     void update()
     50     {
     51         s=n;
     52         if(ch[0]) s+=ch[0]->s;
     53         if(ch[1]) s+=ch[1]->s;
     54     }
     55 }*roo[6],pol[maxn<<3];
     56 
     57 node *newnode()
     58 {
     59     static int cnt=0;
     60     return &pol[cnt++];
     61 }
     62 
     63 void rotate(node *&n,int d)
     64 {
     65     node *k=n->ch[d^1];
     66     n->ch[d^1]=k->ch[d];
     67     k->ch[d]=n;
     68     n->update();
     69     k->update();
     70     n=k;
     71 }
     72 
     73 void insert(node *&n,LL x)
     74 {
     75     if(!n) n=newnode(),n->in(x);
     76     else
     77     {
     78         int d=n->cmp(x);
     79         if(d==-1) ++n->n;
     80         else
     81         {
     82             insert(n->ch[d],x);
     83             if(n->ch[d]->r > n->r)     rotate(n,d^1);
     84         }
     85         n->update();
     86     }    
     87 }
     88 
     89 LL lef(node *&n,LL x)
     90 {
     91     if(!n) return -inf;
     92     if(n->v>x) return lef(n->ch[0],x);
     93     return max(n->v,lef(n->ch[1],x));
     94 }
     95 
     96 LL rig(node *&n,LL x)
     97 {
     98     if(!n) return inf;
     99     if(n->v<=x) return rig(n->ch[1],x);
    100     return min(n->v,rig(n->ch[0],x));
    101 }
    102 
    103 void del(node *&n,LL x)
    104 {
    105     if(!n) return ;
    106     int d=n->cmp(x);
    107     if(d==-1)
    108     {
    109         if(n->n>1) n->n--;
    110         else
    111         {
    112             if(!n->ch[0]) n=n->ch[1];
    113             else if(!n->ch[1]) n=n->ch[0];
    114             else
    115             {
    116                 int f=(n->ch[0]->r < n->ch[1]->r?0:1);
    117                 rotate(n,f);
    118                 del(n->ch[f],x);
    119             }
    120         }
    121     }
    122     else del(n->ch[d],x);
    123     if(n) n->update();
    124 }
    125 
    126 LL gcd(LL a,LL b)
    127 {
    128     if (!b) return a;
    129     else return gcd(b,a%b);
    130 }
    131 
    132 void exgcd(LL a,LL b,LL &x,LL &y)
    133 {
    134     if (b==0) 
    135         x=1LL,y=0LL;
    136     else 
    137         exgcd(b,a%b,y,x),y-=x*(a/b);
    138 }
    139 
    140 LL inv(LL v,LL p)
    141 {
    142     LL x,y,g;
    143     exgcd(v,p,x,y);
    144     g=gcd(x,y);
    145     if (g>1)
    146         return -1;
    147     return (x%p+p)%p;
    148 }
    149 
    150 LL lcm (LL a,LL b)
    151 {
    152     return a/gcd(a,b)*b;
    153 }
    154 
    155 LL solve()
    156 {
    157     for (int i=1;i<=n;i++)
    158     {
    159            LL g=gcd(a[i],gcd(atk[i],p[i]));
    160         atk[i]/=g,p[i]/=g,a[i]/=g;
    161         LL Inv=inv(atk[i],p[i]);
    162         if (Inv<0)
    163         return -1LL;
    164         a[i]=mu(a[i],Inv,p[i]);
    165     }
    166     LL M=p[1],A=a[1],t,d,x,y;
    167     for(int i=2;i<=n;++i)
    168     {
    169         d=gcd(M,p[i]);
    170         exgcd(M,p[i],x,y);
    171         if((a[i]-A)%d)
    172             return -1LL;
    173         t=p[i]/d;
    174            x=mu((a[i]-A)/d,x,t);
    175         x=(x%t+t)%t;
    176            A=(A+mu(M,x,(LL)M/d*p[i]))%((LL)M/d*p[i]);
    177         M=M/d*p[i];
    178        }
    179     A=(A%M+M)%M;
    180     ans=A;
    181     while (ans<minc) ans+=(LL)lcmm;
    182     return ans;
    183 }
    184 
    185 int main()
    186 {
    187     scanf("%d",&T);
    188     for (num=1;num<=T;num++)
    189     {
    190         minc=(LL)-1000;
    191         scanf("%d%d",&n,&m);
    192         for (int i=1;i<=n;++i)
    193             scanf("%lld",&a[i]);
    194         for (int i=1;i<=n;++i)
    195             scanf("%lld",&p[i]);
    196         for (int i=1;i<=n;++i)
    197             scanf("%lld",&g[i]);
    198         for (int i=1;i<=m;++i)
    199         {
    200             scanf("%lld",&x);
    201             insert(roo[num],x);
    202         }
    203         if(n==1) lcmm=p[1];
    204         if(n>=2) lcmm=lcm(p[1],p[2]);
    205         for (int i=3;i<=n;++i)
    206             lcmm=lcm(lcmm,p[i]);
    207         for (int i=1;i<=n;++i)
    208         {
    209             atk[i]=lef(roo[num],a[i]);
    210             if(atk[i]<=0) atk[i]=rig(roo[num],(LL)-1000);
    211             del(roo[num],atk[i]);
    212             if(a[i]%atk[i]==0) minc=max(minc,a[i]/atk[i]);
    213             else               minc=max(minc,a[i]/atk[i]+(LL)1);
    214             insert(roo[num],g[i]);
    215         }
    216         printf("%lld
    ",solve());
    217     }
    218     return 0;
    219 }
    屠龙勇士

      这里强烈谴责T1的大样例,看起来非常庞大,检验性很强,然而我的35代码也可以过这个大样例。T2,T3其实弄点分不是很困难,但是T1写的太久了,最后就没开这两个题。

      Day2得分:35+0+0=35;

      于是这个比赛的得分:100(笔试)+50+35=185...Cu线199,于是就什么都没有啦。

      实力不够是一个问题,但是还有一个问题就是考试的策略非常不合理。有5道题的正解确实是不会,但是唯一想到正解的题也没有写出高分来。

      Day1的三道题都有不该丢的分,T1的65分离线其实想到了,但是因为已经写了一部分BFS舍不得扔掉就没写,5分的在线树剖其实也应该写一写(最后还剩很多时间),T2交错程序...,T3要是手出几组数据可能就想到判重的问题了。不过同步赛选手没有拿到大样例表示体验极差。T2有几个点其实相当于提答,但是也没有想到打打表。

      Day2的T1挂到连暴力分都不如,首先数组只要开的下就尽量往大里开,如果不能保证正解能得分就写几个分段程序稳住分,n=1,m=1,p=1,p是质数的部分分其实都不难写吧。还有一些小一点的数据点其实可以枚举x。这样差不多就有70+了,T2T3连纯暴力都没写,听说2、30分的部分分也不是很难写。于是精神Cu以后做题就有经验了,不要再犯这种错误啦,也要多学点东西。

      ---shzr

  • 相关阅读:
    [原]Linux 命令行浏览器
    Linux 命令行浏览器
    [原]Linux 命令行 发送邮件
    Linux 命令行 发送邮件
    [原]Linux 修改时区
    Linux 修改时区
    [原]Ubuntu 下安装Mongodb
    离线解密RDP凭证密码
    [Win]权限维持
    Nginx反向代理
  • 原文地址:https://www.cnblogs.com/shzr/p/9345576.html
Copyright © 2020-2023  润新知