踩了挺多以前没踩过的坑。。。
T1 一开始是打了一个 60pts 的 DFS ,在与暴力拍了几组数据保证正确性之后,
突然想到 BFS 可能会更快一些,然后就又码了一个 BFS,又和 DFS 拍了200组数据,
发现 BFS 确实快,然后就交了一个 BFS 然后我就直接 \(60pts\rightarrow 0pts\)
后来看了一下特殊性质该了一下边界就 80pts了,肝疼QAQ
然后就是 T3 以前一直是在 结构体里读入的,这回就翻车了。
还有就是 map 的重载小于号最好不要和等于号一样,不然就挂分了。。
官方题解说和蚯蚓有一点像(尽管我没有做过这个题)
其实就是开了 b 个队列,每次选择所有队首元素中最小的就是光滑数。
然后对于编号大于等于刚才所选队列的加入一个新元素就好了。
因为每次都是从小到大,因此可以达到不重复的目的。
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch>'9'||ch<'0') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=7e7+10,M=1e7; int INF; int k,b,ans,pb[20]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47}; set<int> s; void dfs(int x,int cnt) { if(cnt>INF||cnt<0) return ; if(x==b+1) { s.insert(cnt); if(s.size()>k) s.erase((--s.end())); return ; } int pre=cnt; for(int i=1;cnt<=INF&&cnt>0;i++) { dfs(x+1,cnt); cnt=cnt*pb[x]; } } signed main() { b=read(); k=read(); if(b==2) INF=1e18; else INF=7e7; dfs(1,1); printf("%lld",(*s.rbegin())); return 0; }
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch>'9'||ch<'0') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int INF=1e18; queue<int> q[20]; int k,ans,b,pb[20]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47}; signed main() { b=read(); k=read(); for(int i=1;i<=b;i++) q[i].push(pb[i]); while(k!=1) { int minn=INF,id; for(int i=1;i<=b;i++) if(minn>q[i].front()) { minn=q[i].front(); id=i; } q[id].pop(); ans=minn; for(int i=id;i<=b;i++) q[i].push(ans*pb[i]); k--; } printf("%lld",ans); return 0; }
挺好的一个题,主要有两种做法:状压,状压+记忆化DFS
我当然是选择裸的状压了(雾
首先不难发现我们只关心 n 的质因数的种类和数量,因此不需要记录对应的数值。
发现对于所含质因数种类相同的两个数在某种意义上是等价的。
因此我们可以将此压缩成为一类数,记录这一类数的数量就好了。
然后就可以愉快的状压了。
采用 8 进制进行记录,0 表示没有出现过这个质因数
1~6 表示与此数同时计入中编号最小的数的编号
7则表示这个数字出现过两次。
然后就是状压,并对于已经有的状态的基础上进行 DP 就好了。
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch>'9'||ch<'0') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=4e7+10,mod=1e9+7; int n,ans,num[N],t,tot,p[10],q[10],s[N]; int cnt,pri[N]; bool vis[N]; void Prime() { vis[1]=true; for(int i=2;i<=min((int)sqrt(n)+1,40000000ll);i++) { if(!vis[i]) pri[++cnt]=i; for(int j=1;j<=cnt&&i*pri[j]<=min((int)sqrt(n)+1,40000000ll);j++) { vis[i*pri[j]]=true; if(i%pri[j]==0) break; } } } void dfs(int x,int all) { if(x==tot+1) { if(all!=1) num[++t]=all; return ; } dfs(x+1,all); for(int i=1;i<=q[x];i++) { all*=p[x]; dfs(x+1,all); } } int gcd(int x,int y) { if(!y) return x; return gcd(y,x%y); } void dfs2(int pos) { ans=(ans+1)%mod; for(int i=1;i<=t;i++) { int sum=0; for(int j=1;j<pos&&sum<=1;j++) if(gcd(s[j],num[i])!=1) sum++; if(sum<=1) { s[pos]=num[i]; dfs2(pos+1); } } } signed main() { n=read(); Prime(); for(int i=1;i<=cnt&&n!=1;i++) { if(n%pri[i]) continue; p[++tot]=pri[i]; while(n%pri[i]==0) { q[tot]++; n/=pri[i]; } } if(n!=1) p[++tot]=n,q[tot]=1; dfs(1,1); dfs2(1); printf("%lld",ans-1); return 0; }
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch>'9'||ch<'0') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=(1<<18)+10,M=(1<<6)+10,mod=1e9+7; int n,cnt,ans,f[N],s[N],fac[10],sub[M]; signed main() { n=read(); for(int i=2;i<=sqrt(n)+1;i++) if(n%i==0) { cnt++; while(n%i==0) { fac[cnt]++; n/=i; } } if(n!=1) fac[++cnt]=1; fill(sub+1,sub+(1<<cnt)+1,1); for(int i=1;i<(1<<cnt);i++) for(int j=1;j<=cnt;j++) if((i>>j-1)&1) sub[i]*=fac[j]; f[0]=1; for(int i=0;i<(1<<3*cnt);i++) if(f[i]) for(int j=1;j<(1<<cnt);j++) { int pos=0,sta=i,jud=0; bool check=false; for(int k=1;k<=cnt;k++) if(!((i>>(k-1)*3)&7)&&((j>>k-1)&1)) { pos=k; break; } for(int k=1;k<=cnt;k++) if(((i>>(k-1)*3)&7)&&((j>>k-1)&1)) { if(((i>>(k-1)*3)&7)==7||(jud&&jud!=((i>>(k-1)*3)&7))) { check=true; break; } if(!jud) jud=((i>>(k-1)*3)&7); } if(check) continue; for(int k=1;k<=cnt;k++) if((j>>k-1)&1) { if((i>>(k-1)*3)&7) sta|=7<<(k-1)*3; else sta|=pos<<(k-1)*3; } f[sta]=(f[sta]+f[i]*sub[j]%mod)%mod; } for(int i=1;i<(1<<3*cnt);i++) ans=(ans+f[i])%mod; printf("%lld",ans); return 0; }
随机化算法是正解还是第一次见。
随便挑出两组数,然后高斯消元求出来三个操作的数。
然后进行回代看是否有一半以上的数满足条件。
有一个小细节:对于 \([-\dfrac{\pi}{2},\dfrac{\pi}{2}]\)类似的区间里
不同的弧度的 cos 值是相同的,这个时候就需要用 sin 来判断一下了。
#include<bits/stdc++.h> #define int long long using namespace std; const int N=1e5+10;int n; double eps=1e-6,s[10][10]; struct node { double x,y,x2,y2,x3,y3; }q[N]; int random(int l,int r) { int x=rand()*rand()%(r-l+1)+l; while(x<=0) x=rand()*rand()%(r-l+1)+l; return x; } void gaosi(int n,int m) { for(int i=1;i<=n;i++) { int pos=0; for(int j=1;j<m;j++) if(s[i][j]){pos=j;break;} if(s[i][pos]!=1&&s[i][pos]) { double temp=s[i][pos]; for(int j=pos;j<=m;j++) s[i][j]/=temp; } for(int j=i+1;j<=n;j++) { if(!s[j][pos]) continue; double temp=s[j][pos]; for(int k=pos;k<=m;k++) s[j][k]-=s[i][k]*temp; } } for(int i=n;i>=2;i--) { int pos=0; for(int j=1;j<m;j++) if(s[i][j]){pos=j;break;} if(s[i][pos]!=1&&s[i][pos]) { double temp=s[i][pos]; for(int j=pos;j<=m;j++) s[i][j]/=temp; } for(int j=1;j<i;j++) { if(!s[j][pos]) continue; double temp=s[j][pos]; for(int k=pos;k<=m;k++) s[j][k]-=s[i][k]*temp; } } } signed main() { srand((unsigned)time(0)); scanf("%lld",&n); for(int i=1;i<=n;i++) scanf("%lf%lf%lf%lf",&q[i].x,&q[i].y,&q[i].x2,&q[i].y2); while(1) { int i=random(1,n),j=random(1,n),sum=0; if(i==j) continue; s[1][1]=q[i].x;s[1][2]=-q[i].y;s[1][3]=1;s[1][4]=0;s[1][5]=q[i].x2; s[2][1]=q[i].y;s[2][2]=q[i].x;s[2][3]=0;s[2][4]=1;s[2][5]=q[i].y2; s[3][1]=q[j].x;s[3][2]=-q[j].y;s[3][3]=1;s[3][4]=0;s[3][5]=q[j].x2; s[4][1]=q[j].y;s[4][2]=q[j].x;s[4][3]=0;s[4][4]=1;s[4][5]=q[j].y2; gaosi(4,5); double cs,ss,scal,dx,dy,cos,sin,tmp=1; for(int k=1;k<=4;k++) if(fabs(s[k][1])>eps) cs=s[k][5]; for(int k=1;k<=4;k++) if(fabs(s[k][2])>eps) ss=s[k][5]; for(int k=1;k<=4;k++) if(fabs(s[k][3])>eps) dx=s[k][5]; for(int k=1;k<=4;k++) if(fabs(s[k][4])>eps) dy=s[k][5]; scal=sqrt(cs*cs+ss*ss);cos=cs/scal;sin=ss/scal; if(sin<0) tmp=-1; for(int k=1;k<=n;k++) if(fabs((q[k].x*cos-q[k].y*sin)*scal+dx-q[k].x2)<=eps&&fabs((q[k].y*cos+q[k].x*sin)*scal+dy-q[k].y2)<=eps) sum++; if(sum>=(n>>1)){printf("%.11lf\n%.11lf\n%.11lf %.11lf",acos(cos)*tmp,scal,dx,dy);return 0;} } return 0; }