[考试反思]1023csp-s模拟测试84:精妙
Posted hzoi-deepinc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[考试反思]1023csp-s模拟测试84:精妙相关的知识,希望对你有一定的参考价值。
一套很奇怪的题。单调性+神仙dp/搜索+随机化。
但是说实在的,思路都很不错。
考场上显然乱搞没什么好说的。
虽说T2剪枝打错变量名掉了20分。。。
T1:Smooth
暴力各有不同,最暴力的想法就是往队列里不断扔。
有的元素会被扔多次导致队列元素过多。
像线性筛一样,从大到小枚举质因子,保证每个数只会被最大的质因子筛掉。
1 #include<cstdio> 2 #include<queue> 3 using namespace std; 4 priority_queue<long long,vector<long long>,greater<long long> >q; 5 const int P[16]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47}; 6 int main(){//freopen("ex_smooth1.in","r",stdin); 7 int k,b; 8 scanf("%d%d",&b,&k); 9 q.push(1); 10 k--; 11 while(k--){ 12 long long x=q.top();q.pop(); 13 for(int i=b;i;--i)if(x%P[i]==0){q.push(x*P[i]);break;} 14 else q.push(x*P[i]); 15 } 16 printf("%lld ",q.top()); 17 }
像蚯蚓一样,加入队列有单调性,所以不用优先队列。
开b个队列,每个队列i里存最大质因子是pi的数。
1 #include<cstdio> 2 #include<queue> 3 using namespace std; 4 queue<long long>q[16]; 5 const int P[16]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47}; 6 int b,k; 7 long long pop_push(){ 8 long long ans=q[1].front(),bp=1; 9 for(int i=2;i<=b;++i)if(q[i].front()<ans)ans=q[i].front(),bp=i; 10 q[bp].pop(); 11 for(int i=b;i>=bp;--i)q[i].push(ans*P[i]); 12 return ans; 13 } 14 int main(){ 15 scanf("%d%d",&b,&k); 16 for(int i=1;i<=b;++i)q[i].push(P[i]); 17 k-=2; 18 while(k--)pop_push(); 19 printf("%lld ",pop_push()); 20 }
T2:Six
先说暴力?
搜啊,好像没什么好说的。
发现每次加入的数的影响只与它含有哪几种质因子有关。
然后就可以愉快的搜了。
1 #include<cstdio> 2 #define mod 1000000007 3 #define int long long 4 int t[7],knd,al[18];long long n,rate[65]; 5 int sch(int p){//if(p<=3)printf("%d ",p); 6 int ans=1; 7 for(int i=1;i<1<<knd;++i){ 8 int cov=0; 9 for(int j=1;j<p;++j)if(i&al[j])cov++; 10 if(cov>1)continue; 11 al[p]=i; 12 ans=(ans+rate[i]*sch(p+1))%mod; 13 } 14 return ans; 15 } 16 main(){ 17 scanf("%lld",&n); 18 for(long long i=2;i*i<=n;++i)if(n%i==0){ 19 knd++; 20 while(n%i==0)n/=i,t[knd]++; 21 }if(n!=1)t[++knd]=1; 22 for(int i=1;i<1<<knd;++i){ 23 rate[i]=1; 24 for(int j=1;j<=knd;++j)if(i&1<<j-1)(rate[i]*=t[j])%=mod; 25 } 26 printf("%lld ",sch(1)-1); 27 }
%%%ParisB的Six的状态定义。
首先我们可以发现,每个质因子的第一次被加入,至多会分6批。
我们考虑加入一个数的限制。
如果某一种因子已经被两个数包含,你还含有这个因子,就不合法。
如果你含有两个因子A和B,A和B被加入不在同一个数里(就是我上面说的“6批”),那么不合法。
如果在同一批里,那么你就只与那一批的一个数冲突,还是合法的。
所以你就记录一下每一个因子是在第几批加入的。8进制压位。
0:未被加入。
1~6:被在第1~6批加入。
7:已经被加入2次。
然后对于每一种状态,枚举$2^6$种可能的转移,不合法的情况只有上面2种。
总复杂度$2^{18} imes 2^6 imes 6$然而因为合法状态只有2100种,所以最终的复杂度大约是1e6的。
代码极短但是不易压行(否则逻辑极其混乱)
适度的常用位运算宏定义能使代码更加简单而清晰
1 #include<cstdio> 2 #define mod 1000000007 3 #define s1 (k&1<<p-1) 4 #define s2 (j>>3*p-3&7) 5 int dp[266666],t[7],cnt,ans;long long n,rate[66]; 6 int main(){ 7 scanf("%lld",&n); 8 for(long long i=2;i*i<=n;++i)if(n%i==0){ 9 cnt++; 10 while(n%i==0)n/=i,t[cnt]++; 11 }if(n!=1)t[++cnt]=1; 12 for(int i=1;i<1<<cnt;++i){ 13 rate[i]=1; 14 for(int j=1;j<=cnt;++j)if(i&1<<j-1)rate[i]*=t[j]; 15 } 16 dp[0]=1; 17 for(int j=0;j<1<<cnt*3;++j)if(dp[j]){ 18 ans=(ans+dp[j])%mod; 19 for(int k=1;k<1<<cnt;++k){ 20 int x=0,ns=j,m=0; 21 for(int p=1;p<=cnt;++p)if(!s2&&s1){x=p;break;} 22 for(int p=1;p<=cnt;++p)if(s2&&s1) 23 if(s2==7)goto F; 24 else if(!m)m=s2; 25 else if(m!=s2)goto F; 26 for(int p=1;p<=cnt;++p)if(s1) 27 if(s2)ns|=7<<3*p-3; 28 else ns|=x<<3*p-3; 29 dp[ns]=(dp[ns]+dp[j]*rate[k])%mod; F:; 30 } 31 }printf("%d ",ans-1); 32 }
T3:Walker
变化完之后的最终坐标是$(scale cos heta x - scale sin heta y +d_x,scale sin heta x + scale cos heta y +d_y)$
我们把$scale sin heta$和$scale cos heta$看做两个单独的变量,叫$a,b$吧
然后我们随便带两组数据进去就能解出$a,b,d_x,d_y$这四个变量
根据$sin^2 heta +cos^2 heta=1$,得到$a^2+b^2=scale^2$
所以我们就解出了$scale$,同时也就解出了$sin heta$和$cos heta$
然后问题在于怎么解出$ heta$。方法很多,我说一个。
根据$frac{sin heta}{cos heta}=tan heta$我们能知道$tan$值,利用$atan$函数得到一个$ heta$值。
然后我们再用$sin$函数算一下这个$ heta$的正弦值和上面那个$frac{a}{scale}$是否一致。
如果一致,那么就对了,否则加一个$pi$,转半圈就是了。(因为只根据$tan$得到的角度值可能刚好是相反的)
我们对于两个坐标求出的一组解拿去check,如果超过半数都是对的那么就完事,否则继续循环
因为有一半是正确的,所以你选出两个正确坐标的概率是$frac{1}{4}$,不是很小,所以循环不会很多次。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ldb double 4 int n;ldb x[100005],y[100005],_x[100005],_y[100005],mxt[5][6]; 5 void Gauss(){ 6 for(int i=1;i<=4;++i){ 7 ldb mx=mxt[i][i];int mxp=i; 8 for(int j=i+1;j<=4;++j)if(fabs(mxt[j][i])>mx)mx=fabs(mxt[j][i]),mxp=j; 9 if(mxp!=i)for(int j=i;j<=5;++j)swap(mxt[i][j],mxt[mxp][j]); 10 for(int j=4;j>i;--j)for(int k=5;k>=i;--k)mxt[j][k]-=mxt[j][i]/mxt[i][i]*mxt[i][k]; 11 } 12 for(int i=4;i;--i)for(int j=i-1;j;--j)mxt[j][5]-=mxt[i][5]*mxt[j][i]/mxt[i][i]; 13 for(int i=4;i;--i)mxt[i][5]/=mxt[i][i]; 14 } 15 int main(){ 16 scanf("%d",&n);srand(time(0)); 17 for(int i=1;i<=n;++i)scanf("%lf%lf%lf%lf",&_x[i],&_y[i],&x[i],&y[i]); 18 while(1){ 19 int p1=rand()%n+1,p2=rand()%n+1,AC=0; 20 while(p1==p2)p2=rand()%n+1; 21 mxt[1][5]=x[p1];mxt[1][4]=0;mxt[1][3]=1;mxt[1][2]=-_y[p1];mxt[1][1]=+_x[p1]; 22 mxt[2][5]=y[p1];mxt[2][4]=1;mxt[2][3]=0;mxt[2][2]=+_x[p1];mxt[2][1]=+_y[p1]; 23 mxt[3][5]=x[p2];mxt[3][4]=0;mxt[3][3]=1;mxt[3][2]=-_y[p2];mxt[3][1]=+_x[p2]; 24 mxt[4][5]=y[p2];mxt[4][4]=1;mxt[4][3]=0;mxt[4][2]=+_x[p2];mxt[4][1]=+_y[p2]; 25 Gauss(); 26 ldb scale=mxt[1][5]*mxt[1][5]+mxt[2][5]*mxt[2][5];scale=sqrt(scale); 27 ldb cosine=mxt[1][5]/scale,sine=mxt[2][5]/scale,theta=atan(sine/cosine),X=mxt[3][5],Y=mxt[4][5]; 28 if(scale>10||scale<0)continue; 29 if(fabs(sin(theta)-sine)>1e-7)theta+=3.141592653589793238462643383279l; 30 if(theta<-1e-8)theta+=3.141592653589793238462643383279L*2; 31 for(int i=1;i<=n;++i)if(fabs(scale*cos(theta)*_x[i]-scale*sin(theta)*_y[i]+X-x[i])<1e-4&&fabs(scale*sin(theta)*_x[i]+scale*cos(theta)*_y[i]+Y-y[i])<1e-4)AC++; 32 if(AC>=n+1>>1)return printf("%.18lf %.18lf %.18lf %.18lf ",theta,scale,X,Y),0; 33 } 34 }
以上是关于[考试反思]1023csp-s模拟测试84:精妙的主要内容,如果未能解决你的问题,请参考以下文章