模拟32 考试总结
果然挂没了呢。。。
考试经过
得知今天会有十余位外校强者参见考试,令我本已残破不堪的名次雪上加霜。。。
开题认为T1很可做,先搞了40分暴力,然后水特殊性质,接着感觉有了七八十分,似乎不错,于是果断不想正解,于是先做比较水的T3
T3认为可以高斯消元然后应该有不少分,然而由于数学能力为0太久没碰文化课导致无法正确计算出三角函数,忘了c++内置的反三角函数,于是捡30分跑路。。。
T2直接对着2的数据范围大力分类讨论,在结束之前过了样例,然后再从后往前找自己
然而。。。

整挺好
发了疯一样看T1错哪里了,瞪了半天最后看到:PMZG写出了如下程序:

这是一个质数表,还没发现?

于是我不知道41是质数。。
建议重学:小学数学。
嘎
T1.Smooth
不太会正解,但是显然可以乱搞
你可以对着\(7e7\)直接dfs,然后就有好多分
考虑怎么在\(1e18\)的时候不超时,发现只要搜\(1e7\)个数,所以我们可以卡出一个上界,即超过第\(1e7\)个数的大小就停
上界可以直接打表,分别一个一个枚举\(b\),注意打表的时候用上一次打出来的上界去卡,不然根本打不出来
最后把搜出来的数排个序输出就行,由于局部有序,所以归并更快
正解是多队列做法\(O(KB)\) ,我的是\(O(KlogK)\),似乎常数略大,但也可过
#include <bits/stdc++.h>
using namespace std;
//#define int long long
#define R register
int p[20]={0,2, 3, 5, 7, 11,13,17,19,23,29,31,37,41,43,47,53,59,61};
int t[20]={0,59,37,25,21,17,16,14,14,13,12,12,11,11,11,10,9,9};
long long ks[20][100];
long long lim[20]={0,1,999502313552216064,1000000000000000000,1000000000000000000,1000000000000000000,1000000000000000000,1000000000000000000,
1000000000000000000,75897663377817600,3002995871216640,269079750030000,44546112408000,10803567398778,3371226870528,1292766707505};
long long a[50000000];
inline long long ksm(long long x,long long y)
{
long long s=1;
for(;y;y>>=1)
{
if(y&1)s=s*x;
x=x*x;
}
return s;
}
int k,b,num;
inline void dfs(int x,long long sum)
{
if(x>b)
{a[++num]=sum;return;}
for(R int i=0;i<=t[x];i++)
{
long long ga=ks[x][i];
if(lim[b]/(double)ga<sum)break;
long long ans=sum*ga;
dfs(x+1,ans);
}
}
signed main()
{
cin>>b>>k;
for(int i=1;i<=15;i++)
for(int j=0;j<=t[i];j++)
ks[i][j]=ksm(p[i],j);
dfs(1,1);//cout<<q.size();
stable_sort(a+1,a+num+1);
cout<<a[k]<<endl;
return 0;
}
这个故事告诉我们:做人要有梦想
T2.Six
实际上是个状压,还是小数据范围
考虑记忆化搜索,一种朴素的状态设计是\(s,t\)分别表示出现一次的质因子,出现两次的质因子,但会出现一个问题:假如有了\(2,3\),那么再来一个\(6\)显然非法,然而\(6,6\)是合法的,上面无法区分这两种状态,于是考虑更换状态定义建议颓题解
第二维设为当前序列中 出现在两个不同的数中 的质因子对 集合T
实际上是个二维状压,记录每一种配对方案(包括一样的数出现两次),所以有\(5\times 6+6=21\)种状态,用\(map\)存储
这样的正确性在于,每次转移时假设枚举到状态\(i\),那么有效状态\(ss\)就是\(s\land i\),\(n^2\)可以算出\(ss\)包含的质因子对,这个意思是已经出现过至少一次的数,如果质因子对与\(t\)有交,那么证明至少一对(2个)质因子已经出现2次,不合法舍弃
把状态更新为\(t\)或上 \(i\)和\(s\)算出的\(sss\),代表新增的因子对,直接搜就完事
复杂度看似很假实则很假,题解称不会超过\(50000\)种状态,\(O_{能过}\)
这种很神仙很神仙的状态定义要多积累
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int p[10],num[10],t;
inline void divide(int x)
{
for(int i=2;i<=sqrt(x);i++)
{
if(x%i)continue;
p[++t]=i;
while(x%i==0)num[t]++,x/=i;
}
if(x!=1)
p[++t]=x,num[t]++;
}
int id[7][7],tot;
inline void getnum()
{
for(int i=1;i<=t;i++)
for(int j=i;j<=t;j++)
id[i][j]=++tot;
}
inline int ganhash(int l,int s1,int s2)
{
return s2+s1*(1<<22)+l*(1<<30);
}
unordered_map <int,int> mp;int sum[1<<7];
inline int gan(int s1,int s2)
{
int ans=0;
for(int i=1;i<=t;i++)
{
if(!((s1>>(i-1))&1))continue;
for(int j=1;j<=t;j++)
{
if(!((s2>>(j-1))&1))continue;
if(i>j)ans|=(1<<(id[j][i]-1));
else ans|=(1<<(id[i][j]-1));
}
}
return ans;
}
int mem[1<<7][1<<7];
int dfs(int l,int s1,int s2,int lim)
{
int ga=ganhash(l,s1,s2);
if(l>lim)
{mp[ga]=1;return 1;}
if(mp.find(ga)!=mp.end())return mp[ga];
int ans=0;
for(int i=1;i<=(1<<t)-1;i++)
{
int s=i&s1;
int ss=mem[s][s];
if(ss&s2)continue;
ans=(ans+dfs(l+1,s1|i,mem[i][s1]|s2,lim)*sum[i]%mod)%mod;
}
mp[ga]=ans;
return ans;
}
signed main()
{
int n;cin>>n;
divide(n);
for(int i=1;i<=(1<<t);i++)
{
sum[i]=1;
for(int j=1;j<=t;j++)
if((i>>(j-1))&1)sum[i]=sum[i]*num[j]%mod;
}
getnum();
for(int i=1;i<=(1<<t)-1;i++)
for(int j=1;j<=(1<<t)-1;j++)
mem[i][j]=gan(i,j);
int ans=0;
for(int i=1;i<=2*t;i++)
{
mp.clear();
ans=(ans+dfs(1,0,0,i))%mod;
}
cout<<ans<<endl;
return 0;
}
另外手动%付队,感谢其将我从打表的深渊中解救出来
T3.Walker
看上去花里胡哨,实际挺简单
一共就四个变量(\(s\)和三角函数乘起来),两组数据就能解出来,然后代入验证就行,暴力为\(n^3\),但发现至少一半是正确的,所以就考虑随机化:每次选对的概率\(1/4\),那么不对的概率\(3/4\),假如选10次全不对的概率就已经低到了\(1e-5\)左右,可以忽略
如果不相信自己的rp就 while(1) 一直选,没多久就停了
还有需要一些数学技巧文化课菜鸡瑟瑟发抖
c++里面的反三角函数都是\(a\)开头,算出来就是弧度制
最后要判断一下正负,像极了文化课
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=100050;
inline int random(int x)
{
return abs(rand()*rand())%x+1;
}
struct node{
double x,y,xx,yy;
}a[N];
const double exs=1e-6;
inline bool pd(double x,double y)
{
if(x>y)swap(x,y);
if(y-x<exs)return 1;
else return 0;
}
double b[5][5],c[5],d[5],sita,sinn,coss,dx,dy,s;
inline void guass()
{
memset(d,0,sizeof(d));
for(int i=1;i<=4;i++)
{
for(int j=i+1;j<=4;j++)
{
if(fabs(b[j][i])>fabs(b[i][i]))
{
for(int k=i;k<=4;k++)swap(b[i][k],b[j][k]);
swap(c[i],c[j]);
}
}
if(pd(b[i][i],0.0))continue;
for(int j=i+1;j<=4;j++)
{
double p=b[j][i]/b[i][i];
for(int k=i;k<=4;k++)b[j][k]-=b[i][k]*p;
c[j]-=c[i]*p;
}
}
for(int i=4;i>=1;i--)
{
for(int j=4;j>i;j--)
c[i]-=b[i][j]*d[j];
if(pd(b[i][i],0.0))continue;
else d[i]=c[i]/b[i][i];
}
}
inline void get(node aa,node bb)
{
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
memset(d,0,sizeof(d));
b[1][1]=-aa.y;b[1][2]=aa.x;b[1][3]=1;b[1][4]=0;c[1]=aa.xx;
b[2][1]=aa.x;b[2][2]=aa.y;b[2][3]=0;b[2][4]=1;c[2]=aa.yy;
b[3][1]=-bb.y;b[3][2]=bb.x;b[3][3]=1;b[3][4]=0;c[3]=bb.xx;
b[4][1]=bb.x;b[4][2]=bb.y;b[4][3]=0;b[4][4]=1;c[4]=bb.yy;
guass();
dx=d[3],dy=d[4];
s=sqrt(d[1]*d[1]+d[2]*d[2]);
assert(!pd(s,0.0));
sinn=d[1]/s;coss=d[2]/s;
}
inline bool check(node p)
{
double newx=(p.x*coss-p.y*sinn)*s+dx;
double newy=(p.x*sinn+p.y*coss)*s+dy;
if(pd(newx,p.xx)&&pd(newy,p.yy))return 1;
return 0;
}
inline int e(int x)
{
if(x&1)return (x+1)>>1;
return x>>1;
}
inline void ok()
{
sita=acos(coss);
if(!pd(sin(sita),sinn))sita*=-1;
printf("%.12lf\n",sita);
printf("%.12lf\n",s);
printf("%.12lf %.12lf",dx,dy);
exit(0);
}
signed main()
{
int n;cin>>n;
srand((unsigned)time(0));
for(int i=1;i<=n;i++)scanf("%lf%lf%lf%lf",&a[i].x,&a[i].y,&a[i].xx,&a[i].yy);
while(1)
{
int i=random(n),j=random(n);
while(i==j)i=random(n);
get(a[i],a[j]);
int num=0;
for(int k=1;k<=n;k++)if(check(a[k]))num++;
if(num>=e(n))ok();
}
return 0;
}
考试总结
1.不要太相信自己的准确性,一定要打表找质数!!!
2.有空多打打对拍,能拿的分不能挂!
3.对于状态设计的理解还是不够深入,确实有时候比较痛苦的题才能提高能力,希望多多总结吧

浙公网安备 33010602011771号