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\n",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

posted @ 2018-07-21 09:21  shzr  阅读(1090)  评论(0编辑  收藏  举报