noip模拟测试7


T1:求方程ax+by=c的解的个数,若超过65535则输出ZenMeZheMeDuo

  ( T<=10000  -1,000,000 <= a,b,c <= 1,000,000 )

  

  这个形式? 一看不就是扩展欧几里得吗?   ——by 冯神

  然而群我数论最菜......

  这个形式?一看不就是特判水分加暴力吗?  ——by me

  考场上一脸懵比,面向数据编程水了60分......

  

  那说说正解,先想到exgcd,然后脑子……#@¥@&%¥%&%¥@#%……一通乱转,便有了算法雏形

  exgcd求一般情况解 + 一大堆特判

  先看一般情况:

    我们可以用exgcd求出 ax + by = gcd(a,b) 的一组解,而且根据裴蜀定理可以知道,当且仅当 gcd(a,b) | c 时原方程有解

    当有解时,再由exgcd的通解可以知道,解的分布情况应该是一次函数上一些位于第一象限的离散整点,且每隔gcd(a,b)出现一次(如图)

    

    位于一次函数上?那就好办了,只要找到第一象限内最高点和最低点就可以用差值除以gcd(a,b)计算出答案了

 

  那特判的情况呢?

    某大佬指出,a、b、c 三个数分别有正、零、负三种情况,只要分33种情况讨论就好了

    %¥&@%¥@#%&

    不过真正写的时候可以先将三个数全转为正数,然后再打个标记,再exgcd之后再同时反转 (x,a) , (y,b) 就可以了

    其他的特判就不多说了(不想说了)

  最后说一句,只要特判打(shui)的好,正解都是浮云(好吧,其实是在掩饰我写不出正解的事实)

  

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<iostream>
 6 #include<queue>
 7 #include<vector>
 8 #define ll long long
 9 using namespace std;
10 
11 int T;
12 
13 ll a,b,c,x,y,g,tx,ty,bg,ed,ans;
14 
15 void noanswer()
16 {
17     printf("0\n");
18     return;
19 }
20 void toomanyanswer()
21 {
22     printf("ZenMeZheMeDuo\n");
23     return;
24 }
25 
26 ll exgcd(ll aa,ll bb,ll &xx,ll &yy)
27 {
28     if(!bb)
29     {
30         xx=1;yy=0;
31         return aa;
32     }
33     ll g=exgcd(bb,aa%bb,yy,xx);
34     yy-=aa/bb*xx;
35     return g;
36 }
37 
38 bool flaga,flagb;
39 
40 int main()
41 {
42     scanf("%d",&T);
43     while(T--)
44     {
45         flaga=flagb=0;
46         scanf("%lld%lld%lld",&a,&b,&c);
47         if(c<0) a=-a,b=-b,c=-c;
48         if(!a&&!b&&!c)
49             {toomanyanswer();continue;}
50         if(!a&&!b&&c)
51             {noanswer();continue;}
52         
53         if(a<0) flaga=1,a=-a;
54         if(b<0) flagb=1,b=-b;
55         
56         g=exgcd(a,b,x,y);
57         
58         if(c%g!=0) {noanswer();continue;}
59         if(flaga) a=-a,flaga=0,x=-x;
60         if(flagb) b=-b,flagb=0,y=-y;
61         
62         if(!a)
63         {
64             if(y>0) toomanyanswer();
65             else noanswer();
66             continue;
67         }
68         if(!b)
69         {
70             if(x>0) toomanyanswer();
71             else noanswer();
72             continue;
73         }
74         
75         if((a<0&&b>0)||(a>0&&b<0)) {toomanyanswer();continue;}
76         
77         if(a<0) a=-a,b=-b,c=-c;
78         x=x*c/g,y=y*c/g;
79         a/=g,b/=g,c/=g;
80         tx=x%b;
81         while(tx<=0) tx+=b;
82         bg=(c-tx*a)/b;
83         ty=y%a;
84         while(ty<=0) ty+=a;
85         ed=ty;
86         if(bg<ed) {noanswer();continue;}
87         ans=(bg-ed)/a+1;
88         if(ans>65535) toomanyanswer();
89         else printf("%lld\n",ans);
90     }
91     return 0;
92 }
t1 Code

 


 


T2:在坐标平面上,从 ( 0,0 ) 到 ( n,m ) , 恰好走了T步的方案数 (在走够T步之前可以经过点 ( n,m ) ) , 输出答案对MOD取模之后的答案  ( T <= 100,000   -T <= n,m <= T   1 <= MOD <= 109+7) (MOD为若干互不相同质数的乘积)

  哦? 能搜索吗? 不能。

  能dp吗? 醒醒,看看 n,m 范围。

  那就是数学了

  一眼看出是组合数 (然并卵)

  思考后会发现,对于一种方案,实际上是一个长度为T的指令序列,其中有上下左右四种指令

  对于不同的指令序列,都有两种属性——1.各个指令的数量 2.排列的顺序

  简单分析后会发现,若分别设上下左右的数量为a,b,c,d

  则一种合法的恰能到达 ( n,m ) 的指令序列的数量属性一定满足:

  a - b = m , d - c = n ( 不妨设 n,m 均为正 )

  又因为 a+b+c+d = T

  三个方程,四个未知数

  于是我们只需要枚举任意一个未知数,复杂度 O ( T )

  然后再用多重集合的排列来算方案数即可

  愉快的做完了?

 

  哦,××××,MOD不是质数!

  怎么办啊???

  想不出来,还好 %30 数据MOD保证是质数,水完再见

  

  考完后有大佬分享正解

  什么?竟然是中国剩余定理?

  

  于是又学到一种新的思路

  先将模数分解质因数,对于每一个质因子进行一次计算,最后再用中国剩余定理合并这些线性同余方程组。

  (仅适用于满足——MOD为若干互不相同质数的乘积这一性质的取模运算)

  

  那这就简单了,对于每一个质因子跑一次上述%30的算法,分别算出答案,最后再跑一下CRT就行了。

  

  但要注意,对于组合数取模时,当模数小于组合数运算中的阶乘数时,不能直接用阶乘和阶乘逆元计算

  因为大于模数的数阶乘后一定含有该模数,故值一定为0,且并不能通过乘以逆元来还原(并没有逆元)

  这时就要用 Lucas 定理来计算

  

  所以没了

  

  ps:其实还有一种更通用的处理模数的思路,扩展Lucas,这样便可以一次计算出答案了

    但这道题保证了MOD的特殊性,所以可以不用,但如果模数为任意自然数的话就必须用扩展Lucas了

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<iostream>
  6 #include<queue>
  7 #include<vector>
  8 #include<cstdlib>
  9 #define ll long long
 10 using namespace std;
 11 const int MAXT=100005;
 12 
 13 ll t,n,m,p;
 14 
 15 ll a,b,c,d;
 16 
 17 void noanswer()
 18 {
 19     printf("0\n");
 20     exit(0);
 21 }
 22 
 23 ll fac[MAXT],inv[MAXT];
 24 ll pri[30],ans[30],ptot;
 25 
 26 ll qpow(ll x,ll k,ll id)
 27 {
 28     ll ret=1;
 29     while(k)
 30     {
 31         if(k&1) ret=(ret*x)%pri[id];
 32         k>>=1;
 33         x=(x*x)%pri[id];
 34     }
 35     return ret%pri[id];
 36 }
 37 
 38 void first(ll id)
 39 {
 40     ll lim=min((ll)t,pri[id]-1);
 41     inv[0]=fac[0]=fac[1]=1;
 42     for(int i=2;i<=lim;i++)
 43         fac[i]=(fac[i-1]*i)%pri[id];
 44     inv[lim]=qpow(fac[lim],pri[id]-2,id);
 45     for(int i=lim-1;i>=1;i--)
 46         inv[i]=(inv[i+1]*(i+1))%pri[id];
 47 }
 48 
 49 ll CRT()
 50 {
 51     ll mul=1,ret=0;
 52     for(int i=1;i<=ptot;i++)
 53         mul*=pri[i];
 54     ll x,y;
 55     for(int i=1;i<=ptot;i++)
 56     {
 57         ll tmp=mul/pri[i];
 58         y=qpow(tmp,pri[i]-2,i);
 59         ret=(ret+y*tmp%mul*ans[i]%mul)%mul;
 60     }
 61     return (ret%mul+mul)%mul;
 62 }
 63 
 64 ll C(ll x,ll y,ll id)
 65 {
 66     if(x<y) return 0;
 67     return fac[x]*inv[y]%pri[id]*inv[x-y]%pri[id];
 68 }
 69 
 70 ll Lucas(ll x,ll y,ll id)
 71 {
 72     if(x<y) return 0; if(!x) return 1;
 73     return Lucas(x/pri[id],y/pri[id],id)*C(x%pri[id],y%pri[id],id)%pri[id];
 74 }
 75 
 76 int main()
 77 {
 78     scanf("%lld%lld%lld%lld",&t,&p,&n,&m);
 79     m=abs(m),n=abs(n);
 80     if(t<n+m) noanswer();
 81     if((t&1)!=((n+m)&1)) noanswer();
 82     
 83     for(ll i=2;i*i<=p;i++)
 84         if(p%i==0)
 85         {
 86             p/=i;
 87             pri[++ptot]=i;
 88         }
 89     if(p>1) pri[++ptot]=p;
 90     
 91     for(int i=1;i<=ptot;i++)
 92     {
 93         first(i);
 94         for(int j=m;;j++)
 95         {
 96             a=j;
 97             b=a-m;
 98             c=(t-2*a+n+m)/2;
 99             d=c-n;
100             if(d<0) break;
101             ans[i]=(ans[i]+Lucas(t,a,i)*Lucas(t-a,b,i)%pri[i]*Lucas(t-a-b,c,i)%pri[i])%pri[i];
102         }
103     }
104     printf("%lld\n",CRT());
105     return 0;
106 }
107     
t2 Code

 


T3:

  考场上脑子没有,只打的60的暴力

  结果发现正解就是优化的暴力

  

  先说说暴力:

    模拟,没了

  再说说正解:

    同一斜线的障碍存在一个vector(或set)里,每次搜的时候二分找下一个障碍,复杂度对了,没了

    

  懂了吗? :)

 

  那详细说说:

    对于同一条斜线上的点,一定满足 x + y 或 x - y 为定值,如图:

  

  首先把斜线分成两种,一种为和相同,另一种为差相同

  对于每个斜线开一个vector,将障碍点的横或纵坐标扔进去(横纵坐标均可,因为我们只需要一个可以比较其相对位置的元素)

  那么这样就可以解决存不下图的问题了,

  在搜索(模拟)的时候,只需要在当前斜线的vector中二分找到下一个障碍,再判断如何转向便可

  而答案则是加上走过的格子即可

  

  特别的,对于180o转向来说,最多只会出现两次,所以我们搜到的时候只需要跳出递归,再从起点反向搜索一遍,最后将答案-1(起点算了两遍)

  

  但如果没有上述特殊情况,又怎么解决何时跳出的问题呢?

  最开始想的是将起始点设为障碍,想想发现不太好写(可能吧)

  之后发现其实从路径中的任何一个点出发都是等价的,所以我们可以在搜索前先走一步(找下一个障碍),然后在转向后的位置再开始搜索

  这样之后一定可以搜到这个点,所以当搜到时return就行了

  

  所以又没了

  

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<iostream>
  6 #include<queue>
  7 #include<vector>
  8 #include<cstdlib>
  9 #define ll long long
 10 using namespace std;
 11 const int MAXN=100005;
 12 
 13 int n,m,k,bx,by,p,D,nx,ny;
 14 
 15 ll ans;
 16 
 17 int dx[5]={0,-1,-1,1,1},dy[5]={0,1,-1,1,-1};
 18 int pos[5]={0,4,3,2,1},pos1[5]={0,3,4,1,2},pos2[5]={0,2,1,4,3};
 19 
 20 char op[5];
 21 
 22 vector<int> t1[2*MAXN],t2[2*MAXN];
 23     // t1 -> 差 \  ;  t2 -> 和 /
 24 
 25 void first()
 26 {
 27     if(op[0]=='N')
 28     {
 29         if(op[1]=='E') p=1;
 30         else p=2;
 31     }
 32     else
 33     {
 34         if(op[1]=='E') p=3;
 35         else p=4;
 36     }
 37     for(int i=0;i<=n+1;i++)
 38     {    
 39         t1[i-0+D].push_back(i);
 40         t2[i+0].push_back(0);
 41         t1[i-(m+1)+D].push_back(i);
 42         t2[i+(m+1)].push_back(m+1);
 43     }
 44     for(int i=1;i<=m;i++)
 45     {
 46         t1[0-i+D].push_back(0);
 47         t2[0+i].push_back(i);
 48         t1[n+1-i+D].push_back(n+1);
 49         t2[n+1+i].push_back(i);
 50     }
 51     for(int i=D-m-1;i<=D+n+1;i++)
 52         sort(t1[i].begin(),t1[i].end());
 53     for(int i=0;i<=n+m+2;i++)
 54         sort(t2[i].begin(),t2[i].end());
 55 }
 56 
 57 bool flag;
 58 
 59 void get_nxt(int x,int y,int o)
 60 {
 61     if(o==1)
 62     {
 63         int tmp=upper_bound(t2[x+y].begin(),t2[x+y].end(),y)-t2[x+y].begin();
 64         ny=t2[x+y][tmp];
 65         nx=x+y-ny;
 66         ans+=ny-y;
 67     }
 68     if(o==2)
 69     {
 70         int tmp=upper_bound(t1[x-y+D].begin(),t1[x-y+D].end(),x)-t1[x-y+D].begin();
 71         nx=t1[x-y+D][tmp-1];
 72         ny=nx-x+y;
 73         ans+=x-nx;
 74     }
 75     if(o==3)
 76     {
 77         int tmp=upper_bound(t1[x-y+D].begin(),t1[x-y+D].end(),x)-t1[x-y+D].begin();
 78         nx=t1[x-y+D][tmp];
 79         ny=nx-x+y;
 80         ans+=nx-x;
 81     }
 82     if(o==4)
 83     {
 84         int tmp=upper_bound(t2[x+y].begin(),t2[x+y].end(),y)-t2[x+y].begin();
 85         ny=t2[x+y][tmp-1];
 86         nx=x+y-ny;
 87         ans+=y-ny;
 88     }
 89 }
 90 
 91 bool get(int x,int y)
 92 {
 93     int t=lower_bound(t1[x-y+D].begin(),t1[x-y+D].end(),x)-t1[x-y+D].begin();
 94     return t1[x-y+D][t]==x;
 95 }
 96 
 97 int cnt;
 98 
 99 void dfs(int x,int y,int o)
100 {
101     if(x==bx&&y==by)
102         if(++cnt>1) return;
103     get_nxt(x,y,o);
104     if(get(nx,ny-dy[o])^get(nx-dx[o],ny))
105     {
106         if(get(nx,ny-dy[o])) dfs(nx-dx[o],ny,pos1[o]);
107         else dfs(nx,ny-dy[o],pos2[o]);
108     }
109     else flag=1;
110 }
111 
112 
113 int main()
114 {
115     scanf("%d%d%d",&n,&m,&k);
116     D=max(n,m);
117     
118     for(int i=1,aa,bb;i<=k;i++)
119     {
120         scanf("%d%d",&aa,&bb);
121         t1[aa-bb+D].push_back(aa);
122         t2[aa+bb].push_back(bb);
123     }
124     scanf("%d%d%s",&bx,&by,op);
125     first();
126     
127     get_nxt(bx,by,p);
128     if(get(nx,ny-dy[p])^get(nx-dx[p],ny))
129     {
130         if(get(nx,ny-dy[p])) bx=nx-dx[p],by=ny,p=pos1[p];
131         else bx=nx,by=ny-dy[p],p=pos2[p];
132     }
133     else bx=nx-dx[p],by=ny-dy[p],p=pos[p];
134     
135     ans=0;
136     
137     dfs(bx,by,p);
138     if(flag) cnt=0,dfs(bx,by,pos[p]),ans--;
139     printf("%lld\n",ans);
140     return 0;
141 }
t3 Code

 


 

posted @ 2019-07-22 21:00  G_keng  阅读(377)  评论(6编辑  收藏  举报