[考试反思]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 }
T80

像蚯蚓一样,加入队列有单调性,所以不用优先队列。

开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 }
View Code

 

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 }
T74

%%%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 }
825B。快去%ParisB

 

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 }
View Code

 

以上是关于[考试反思]1023csp-s模拟测试84:精妙的主要内容,如果未能解决你的问题,请参考以下文章

[考试反思]1002csp-s模拟测试57:平庸

[考试反思]1006csp-s模拟测试61:休止

[考试反思]1110csp-s模拟测试108:消遣

[考试反思]1022csp-s模拟测试82:奇异

[考试反思]1004csp-s模拟测试59:惊醒

[考试反思]1112csp-s模拟测试111:二重