[考试反思]0411省选模拟68:毒瘤
受不了受不了,毒瘤大合集。
卡精打表结论题,自闭专用。。。
大概也凑和,没想研究$T1$,$T2$打了半天表最后给结论跪了,$T3$结论题没猜中结论也就没什么好说的。
$T2$暴力$n^2$打表懒得尝试$65536$的了(电脑容易卡死)结果只得了朴素暴力的分。。。多打两项就行了。。。
我在想出题人怎么好意思:首先我们只要猜到一个结论然后就很好做了。我不明白你们分为什么这么低。。。
这套题挺神仙的,但是就是不适合作为考场上的题。。。
T1:Line
大意:若干直线和一个矩形,求有多少对直线的交点在矩形内。直线均为$y=ax+b(a \neq 0)$的形式。$a\le 10^9,b \le 10^{18},n \le 10^5$
直线与矩形只有三种关系:相离,在角上相切,或有两个交点。
第一类直接扔掉。第二类把一个交点当成两个然后和第三类一起处理。
我们可以根据两个交点得到直线之间的关系。
我们从矩形左下角开始逆时针转一圈,这样矩形边框上的每一个点都对应这一个距离值。
然后每条直线都对应着一个区间。两条直线有交点当且仅当区间有交。
二维数点也即$l_i\le l_j \le r_i \le r_j$。按照左端点排序之后,右端点在当前即将加入的区间中的线段都会贡献答案。
在那之前需要浮点数离散化。不知道为啥被蛇精病一样的卡精,$mod$一发牛爷爷的高精小数。原来并不难写。。。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define S 300005 5 int n,a[S],l,r,N,T[S],C; ll ans,b[S],u,d; 6 void add(int p){for(;p<=C;p+=p&-p)T[p]++;} 7 int ask(int p,int a=0){for(;p;p^=p&-p)a+=T[p];return a;} 8 struct ls{int l,r;friend bool operator<(ls a,ls b){return a.l<b.l||(a.l==b.l&&a.r<b.r);}}L[S]; 9 struct ld{ 10 ll i,f; 11 void set(ll a,ll b){if(a<0&&b<0)a*=-1,b*=-1;i=a/b;f=(a-i*b)*1000000000/b;} 12 friend bool operator<(ld x,ld y){return x.i!=y.i?x.i<y.i:x.f<y.f;} 13 friend bool operator<=(ld x,ld y){return x.i!=y.i?x.i<y.i:x.f<=y.f;} 14 }P[3][S],t[S]; 15 void inter(ll a,ll b){ 16 ll dt=0; int in=0; ld x,D,L,R,U;D.set(d,1),U.set(u,1),L.set(l,1),R.set(r,1); ++N; 17 x.set(a*l+b,1); if(D<=x&&x<U)x.i-=d,P[++in][N]=x; dt+=u-d; 18 x.set(u-b,a); if(L<=x&&x<R)x.i+=dt-l,P[++in][N]=x; dt+=r-l; 19 x.set(a*r+b,1); if(D<x&&x<=U)x.i*=-1,x.f*=-1,x.i+=u+dt,P[++in][N]=x; dt+=u-d; 20 x.set(d-b,a); if(L<x&&x<=R)x.i*=-1,x.f*=-1,x.i+=r+dt,P[++in][N]=x; 21 if(!in)N--; if(in==1)P[2][N]=P[1][N]; 22 } 23 int main(){int _;cin>>_;while(_--){ 24 cin>>n>>l>>d>>r>>u; N=ans=C=0; 25 for(ll i=1,a,b;i<=n;++i)scanf("%lld%lld",&a,&b),inter(a,b); 26 for(int i=1;i<=N;++i)t[++C]=P[1][i],t[++C]=P[2][i]; 27 sort(t+1,t+1+C); int z=C;C=0; 28 for(int i=1;i<=z;++i)t[++C]=t[i]; 29 for(int i=1;i<=N;++i)L[i]=(ls){lower_bound(t+1,t+1+C,P[1][i])-t,lower_bound(t+1,t+1+C,P[2][i])-t}; 30 sort(L+1,L+1+N); 31 for(int i=1;i<=C;++i)T[i]=0; 32 for(int i=1;i<=N;++i)ans+=ask(L[i].r)-ask(L[i].l-1),add(L[i].r); 33 cout<<ans<<endl; 34 }}
T2:Equation
大意:多测,问对于给定$n$,$(x^2+y^2 \mod n,xy \mod n)$这种$pair$有多少种。$T \le 100,n \le 10^{18}$
单参数题直接考虑打表。写个$O(n^2)$然后开始找规律。
看着题目蛮数论的样子,于是从质数开始研究,可以发现$f(p)=(\frac{p+1}{2})^2$。但是对$p=2$并不成立。
继续尝试$f(p^2)=(\frac{p^2-p+2}{2})$。对$p=2$依旧不成立。
像我这种比较笨的打到$p^5$才找到规律:$f(p^e)=(\lfloor \frac{2+ \sum\limits_{i=1}^{e} p^i (-1)^{e-i} }{2}\rfloor)^2$
对所有的$p=2$均不成立。
接着打$f(p_1p_2)$的规律,用$3 \times 5,3 \times 7,5 \times 7$之类的弄一弄发现$f(p_1p_2)=f(p_1)f(p_2)$
发现答案是积性的。这一点对于$p_1=2$也成立。
上面这一些还是大概可以说明的,用$CRT$那种思想,在两个不同的$p_1,p_2$下的一种$pair$唯一确定的对应这一个新的$pair$
接下来是$f(2^e)$这个世界未解之谜。
如果你用暴力$O(n^2)$跑出了不足$16$项,也就是$65536$,那么想到这里和暴力是一个分。
如果你用暴力跑出了$65536$那你就有$30$分。
如果你瞄了一眼$std......$
我觉得题解是在扯淡了,并看不出什么线性递推。
我花了将近两小时解递推系数结果毛线都没弄出来。
设$g(e)=\sum\limits_{i=0}^{2i \le e-3} 2^{e-2i}$
答案是一个与$g(e)^2 ,2^eg(e),4^e,2^e$一次相关的式子且需要分奇偶讨论。
如果你能猜到答案与这些值相关,那么暴力跑出的$14$项足以解出其系数。
那么剩下的问题就只剩下了一个质因数分解。写一个泼辣的肉即可。
这题没啥意义。就当是拿来练习$Pollard \ Rho$了吧。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const unsigned int b[]={1,3,5,11,29,91,317,1211,4669,18491,73277,292411,1166909,4664891,18648637,74583611,298290749}; 5 map<ll,int>M; 6 ll n;int ans,c=0,mod,pw[133]; 7 ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} 8 ll mul(ll a,ll b,ll m){return ((ll)((unsigned ll)a*b-(unsigned ll)(1.L*a/m*b)*m)+m)%m;} 9 ll qp(ll b,ll t,ll m,ll a=1){for(;t;t>>=1,b=mul(b,b,m))if(t&1)a=mul(a,b,m);return a;} 10 bool Miller_Rabin(ll x,int b){ll k=x-1; 11 while(!(k&1)){ 12 ll y=qp(b,k,x); 13 if(y!=x-1&&y!=1)return 0; 14 if(y==x-1)return 1;k>>=1; 15 }return 1; 16 } 17 bool isp(ll x){ 18 if(x==2||x==3||x==5||x==7||x==11||x==13||x==61)return 1; 19 if(x==1||x%2==0||x%3==0||x%5==0||x%7==0||x%11==0||x%13==0)return 0; 20 return Miller_Rabin(x,2)&&Miller_Rabin(x,11)&&Miller_Rabin(x,61); 21 } 22 ll Pollard_Rho(ll x){ 23 ll s=0,t=0,c=rand()%(x-1)+1,p=1,d; 24 for(int g=1;;g<<=1,s=t,p=1){ 25 for(int _=1;_<=g;++_){ 26 t=mul(t,t,x)+c; t-=t>=x?x:0; p=mul(p,t>s?t-s:s-t,x); 27 if(_%127==0){d=gcd(x,p);if(d>1)return d;} 28 }d=gcd(x,p);if(d>1)return d; 29 } 30 } 31 void Div(ll x){ 32 if(x<2)return; 33 if(isp(x)){M[x]++;return;} 34 ll p=Pollard_Rho(x);Div(p);Div(x/p); 35 } 36 int cal(int t){ 37 if(t<4)return b[t]%mod; 38 int r=2; 39 for(int i=t-3;i>=0;i-=2)r=(r+(1ll<<i))%mod; 40 return (((r+4ll+(t&1))*(r-pw[t-3])+pw[2*t-4]-3-6*(t&1))%mod+mod)%mod; 41 } 42 int main(){ 43 pw[0]=1; 44 int t;cin>>t;while(t--){ 45 cin>>n>>mod;c=0; 46 for(int i=1;i<=120;++i)pw[i]=pw[i-1]*2%mod; 47 while(n%2==0)n>>=1,c++; ans=cal(c); 48 M.clear(); Div(n); 49 for(auto P:M){ 50 long long d=1,tot=0,i=P.first;int nt=1,c=P.second; 51 while(c--)d*=i; 52 while(d!=1)tot+=d*nt,d/=i,nt*=-1; 53 tot=(tot+2>>1)%mod; 54 ans=ans*1ll*tot%mod*tot%mod; 55 }cout<<ans<<endl; 56 } 57 }
T3:Bridge
大意:求有多少$n$点无向图,满足$(i,j,k)$之间恰有两条边的$tuple$(称为桥)不超过$k$个。$n \le 5000,k\le 8$
首先想一下部分分$k=0$。没有桥。那么发现对于一个联通块,它一定是完全图。
稍微归纳着来一下,$n\le 2$显然。在此基础上再添加一个点,对于图中已有的边$(a,b)$,新加的点要么同时向它们连边,要么同时不连。
不连就不联通了,矛盾。故每次加入点的时候都要向所有的点连边。
所以问题在于将$n$个带标号的点分为任意个完全图。是贝尔数的含义,直接斯特林数爆干就好了。
把上面这个结论稍微扩展一下,假如我们现在使一个联通图有一个桥了,然后要让新加入一个点。
如果原来桥的三元组是$(a,b,c)$,那么考虑新加入的点与这三个点的连边情况,不论怎么连,至少会多出一个桥。
而如果一个联通图里有桥,我们可以找到其中一个桥,视其他点都是后来逐个加入的。
那么最开始就是$3$个点$1$座桥,每次加入一个点至少多出一个桥,所以因为桥的数量上线是$k$,所以点的上限也只有$k+2$
这个数据范围已经很小了,就可以本机爆搜打表找到$a[i][j]$表示$i$点$j$桥的联通图有多少个。
搜索需要一定技巧,首先最弱智的是如果当前已知的桥数$>k$了那就剪枝掉。
另一方面我们加边的顺序应该是$(1,2),(1,3),(2,3),(1,4)(2,4),(3,4),(1,5),(2,5),(3,5),(4,5)...$这样的。
如此,我们在加入一条边$(l,r)$时,对于所有$i<l$,$(i,l,r)$这个三元组是不是桥就已经确定下来了。
直接$O(n)check$。然后这样就能让上一个弱智剪枝的效果强很多。$10$点$8$桥搜$3min$也就出来了。
得到$a$数组之后我们就只需要一个$dp$。设$dp[i][j]$表示$i$点$j$桥的方案数。
因为点是带标号的,所以转移的时候需要带上组合数:我们钦定新加入的一个联通子图中必然包含未加入的点中编号最小的一个。这样转移就不会重了。
直接这么做复杂度是$O(n^2k^2)$的。显然过不去。(虽说最开始我也有一点信仰
然后我们发现,其实有桥的图很少。我们把所有没有桥的联通子图单独提出来。
设$dp[i][j]$表示$i$点$j$桥且每个联通块中都有桥的方案数。这个的转移是$O(16 k^4)$
最后累加答案的时候只要乘上一个组合数,对于剩下的点乘上一个上面部分分说的贝尔数就可以了。这里是$O(nk)$的
复杂度瓶颈在于预处理贝尔数。我写的是$O(n^2)$的。也许有更好的方法?
1 #include<bits/stdc++.h> 2 using namespace std; 3 int mp[11][11],n,k,ans,mod,cnt,al[11],T,dp[50][9],C[5005][5005],B[5005]; 4 const int a[11][9]={ 5 {0,0,0,0,0,0,0,0,0}, 6 {0,0,0,0,0,0,0,0,0}, 7 {0,0,0,0,0,0,0,0,0}, 8 {0,3,0,0,0,0,0,0,0}, 9 {0,0,30,4,3,0,0,0,0}, 10 {0,0,0,150,225,162,150,30,0}, 11 {0,0,0,0,975,1980,2370,3780,5490}, 12 {0,0,0,0,0,7203,26460,28290,58275}, 13 {0,0,0,0,0,0,58940,338520,528360}, 14 {0,0,0,0,0,0,0,534348,4492152}, 15 {0,0,0,0,0,0,0,0,5353965} 16 }; 17 void dfs(int p){ 18 al[p]=T;cnt++; 19 for(int i=1;i<=n;++i)if(mp[p][i]|mp[i][p]&&al[i]!=T)dfs(i); 20 } 21 void sch(int l,int r,int bri){ 22 if(bri>k)return; 23 if(l==r)l=1,r++; 24 if(r==n+1){if(bri==k)cnt=0,T++,dfs(1),ans+=cnt==n;return;} 25 mp[l][r]=0; 26 int x=bri; 27 for(int i=1;i<l;++i)if(mp[i][l]&mp[i][r])x++; 28 sch(l+1,r,x); 29 mp[l][r]=1; 30 for(int i=1;i<l;++i)if(mp[i][l]^mp[i][r])bri++; 31 sch(l+1,r,bri); 32 } 33 int mo(int a){return a>=mod?a-mod:a;} 34 int main(){ 35 cin>>n>>k>>mod; 36 dp[0][0]=1; 37 // for(int i=1;i<=n;++i)B[i]=1; 38 for(int i=B[0]=C[0][0]=1;i<=n;++i)for(int j=1;j<=i;++j)C[i][j]=(C[i-1][j-1]+C[i-1][j]*1ll*j)%mod,B[i]=mo(B[i]+C[i][j]); 39 for(int i=0;i<=n;++i)for(int j=C[i][0]=1;j<=i;++j)C[i][j]=mo(C[i-1][j-1]+C[i-1][j]); 40 for(int i=1;i<=40;++i)for(int j=1;j<=k;++j)for(int l=1;l<=10&&l<=i;++l)for(int x=j;x;--x) 41 dp[i][j]=(dp[i][j]+1ll*C[i-1][l-1]*dp[i-l][j-x]%mod*a[l][x])%mod; 42 for(int i=0;i<=k;++i)for(int j=0;j<=40&&j<=n;++j)ans=(ans+1ll*dp[j][i]*C[n][j]%mod*B[n-j])%mod; 43 cout<<ans<<endl; 44 }