模拟32 考试总结

果然挂没了呢。。。

考试经过

得知今天会有十余位外校强者参见考试,令我本已残破不堪的名次雪上加霜。。。
开题认为T1很可做,先搞了40分暴力,然后水特殊性质,接着感觉有了七八十分,似乎不错,于是果断不想正解,于是先做比较水的T3
T3认为可以高斯消元然后应该有不少分,然而由于数学能力为0太久没碰文化课导致无法正确计算出三角函数,忘了c++内置的反三角函数,于是捡30分跑路。。。
T2直接对着2的数据范围大力分类讨论,在结束之前过了样例,然后再从后往前找自己
然而。。。
image
整挺好
发了疯一样看T1错哪里了,瞪了半天最后看到:PMZG写出了如下程序:
image
这是一个质数表,还没发现?
image
于是我不知道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.对于状态设计的理解还是不够深入,确实有时候比较痛苦的题才能提高能力,希望多多总结吧

posted @ 2021-08-09 14:13  D'A'T  阅读(45)  评论(0)    收藏  举报