3.18省选模拟
$T1$
大概就是找一个最小的半径,使得生成的椭圆能包含所有点
首先由于生成椭圆不是水平的,可以考虑先把每个点顺时针旋转然后让椭圆的长半轴转到$x$轴上
然后把$x$轴缩放就好了,考虑一下,我们现在有一个椭圆包含了所有点,那么我们仅仅需要一个相对关系,那么当$x$都缩放之后,最小椭圆仍然满足条件,并且长度成为原来的$\frac{1}{p}$,到现在就是找一个最小的圆能够覆盖所有的点了
然后跑一个最小圆覆盖就好了
最小圆覆盖,可以用随机增量法,就是我们现在已经已经得到了前$i-1$个点的外接圆
这个时候我们新加入一个点$i,$这个时候需要得到一个新的外接圆
如果这个时候$i$已经在外接圆里了,就直接$continue,$显然这个圆不能增大或变小,如果不在圆内
首先这个点必然在圆上如果不在圆上的话,就说明原来的点形成的圆可以包含它,不成立
那么我们现在有了圆上的一点,我们就可以枚举另外两点来三点确定一个圆
大概过程就是,先把圆心定到$i$点,然后枚举第二个点,确定圆心和半径,最后枚举第三个点求个外接圆(把所有点都包含)就好了
表面复杂度是$n^3$,均摊$O(n)$不知道为什么...
#include<bits/stdc++.h> #define MAXN 1000005 using namespace std; const double PI=acos(-1); struct vec { double x,y; }poz[MAXN]; double du,r,p; int n; double Dis(vec A,vec B) { return sqrt(pow(A.x-B.x,2)+pow(A.y-B.y,2)); } vec SDY(vec A,vec B,vec C) { double a=A.x-B.x; double b=A.y-B.y; double c=A.x-C.x; double d=A.y-C.y; a*=-1; b*=-1; c*=-1; d*=-1; double e=(pow(A.x,2)-pow(B.x,2)-pow(B.y,2)+pow(A.y,2))/2; double f=(pow(A.x,2)-pow(C.x,2)-pow(C.y,2)+pow(A.y,2))/2; return (vec){(d*e-b*f)/(b*c-a*d),(a*f-c*e)/(b*c-a*d)}; } vec rtt(vec a,double altha) { altha*=-1; return (vec){a.x * cos(altha) - a.y * sin(altha),a.x * sin(altha) + a.y *cos(altha)}; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%lf%lf",&poz[i].x,&poz[i].y); } scanf("%lf%lf",&du,&p); for(int i=1;i<=n;i++) { poz[i]=rtt(poz[i],du/180.0*PI); poz[i].x/=p; } random_shuffle(poz+1,poz+1+n); vec yuan; yuan.x=0,yuan.y=0; double r2=0,nx,ny; for(int i=1;i<=n;i++) { if(Dis(poz[i],yuan)>r) { yuan=poz[i]; r=0; //其实这一步我们是确定第二个点的位置 //我们就是枚举三个点使他构成圆 //如果没有包含所有点就重构 for(int j=1;j<i;j++) { if(Dis(yuan,poz[j])>r) { yuan.x=(poz[i].x+poz[j].x)/2.0; yuan.y=(poz[i].y+poz[j].y)/2.0; r=Dis(poz[i],poz[j])/2.0; //并不是很理解这一步有什么用 for(int k=1;k<j;k++) { if(Dis(poz[k],yuan)>r) { yuan=SDY(poz[i],poz[j],poz[k]); r=Dis(poz[k],yuan); } } } } } } printf("%.3f",r); }
$T2$
枚举一条直线把集合分成两个部分
显然每种情况都是不重不漏的被计算进去了,而且所有的情况都可以由一条直线化成两个集合来计算
那么就枚举直线来计算方案,就是说分成两个集合后必然界限分明,那么就可以枚举界限计算了
$T3$
回文自动机
其实就是求另一个串和另一个串的最长公共回文串
回文自动机$PAM$
从学了之后一直没有用过的科技
回文自动机的构造
奇根和偶根
奇根的编号$0,fail$指向奇根
偶根的编号$1,fail$指向自身
其实和$AC$自动机一样,我们$AC$自动机能识别所有的回文串
$fail$指针也就是相当于$AC$自动机上的最长公共后缀一样,这个表示的是最长回文后缀
然后每一条路径都表示一个原字符串的一个回文串,其实这个就相当于把字符串的所有子串都插进去就好了
感觉这个有种既像$AC$自动机又像$SAM$的感觉
我们每次插入一个新节点,就是一直跳$fail$找到能识别他这个节点为后缀节点的回文串就好了
#include<bits/stdc++.h> #define MAXN 11000000 using namespace std; char s[MAXN]; int fail[MAXN],len[MAXN],num[MAXN],tr[MAXN][30],ls=0,now=0,tot=1; int get(int i,int x) { while(i-len[x]-1<0||s[i-len[x]-1]!=s[i]) x=fail[x]; return x; } int main() { scanf("%s",s); fail[0]=1;len[1]=-1; for(int i=0,poz;s[i];i++) { if(i!=0) s[i]=(s[i]-97+ls)%26+97; poz=get(i,now); if(!tr[poz][s[i]-'a']) { fail[++tot]=tr[get(i,fail[poz])][s[i]-'a']; tr[poz][s[i]-'a']=tot; len[tot]=len[poz]+2; num[tot]=num[fail[tot]]+1; } now=tr[poz][s[i]-'a']; cout<<(ls=num[now])<<" "; } }
然后$T3$就和切菜一样了
#include<bits/stdc++.h> #define MAXN 1100000 using namespace std; char s[MAXN]; int fail[MAXN],len[MAXN],num[MAXN],tr[MAXN][30],Ed[MAXN],ls=0,now=0,tot=1; int head[MAXN],nxt[MAXN],to[MAXN],fa[MAXN][26],siz,l1,r1,l2,r2,n,q,dep[MAXN]; bool flag; void add(int u,int v) { siz++; to[siz]=v; nxt[siz]=head[u]; head[u]=siz; } int get(int i,int x) { while(i-len[x]-1<0||s[i-len[x]-1]!=s[i]) x=fail[x]; return x; } void dfs(int now,int f) { fa[now][0]=f; dep[now] = dep[f] + 1; for(int i=head[now];i;i=nxt[i]) { int y=to[i]; if(y==f) continue; dfs(y,now); } } int lca(int x, int y) { if(dep[x] < dep[y]) swap(x, y); for(int i = 20; i >= 0; --i) if(dep[fa[x][i]] >= dep[y]) x = fa[x][i]; if(x ==y) return x; for(int i = 20; i >= 0; --i) { if(fa[x][i] != fa[y][i]) {x = fa[x][i], y = fa[y][i]; } } if(fa[x][0]==-1) flag=1; return fa[x][0]; } int main() { scanf("%d%d",&n,&q); scanf("%s",s); fail[0]=1;len[1]=-1; add(1,0); for(int i=0;i<n;i++) { s[i+n]=s[n-i-1]; } for(int i=0,poz;s[i];i++) { poz=get(i,now); if(!tr[poz][s[i]-'a']) { fail[++tot]=tr[get(i,fail[poz])][s[i]-'a']; tr[poz][s[i]-'a']=tot; add(fail[tot],tot); len[tot]=len[poz]+2; // num[tot]=num[fail[tot]]+1; } now=tr[poz][s[i]-'a']; Ed[i]=now; } memset(fa,-1,sizeof(fa)); dfs(1, -1); for(int i=1;i<=20;i++) { for(int j=0;j<=tot;j++) { fa[j][i]=fa[fa[j][i-1]][i-1]; } } for(int i=1,ed1,ed2,Len;i<=q;i++) { scanf("%d%d%d%d",&l1,&r1,&l2,&r2); Len=min(r1-l1+1,r2-l2+1); r1=2*n-r1+1; l1=2*n-l1+1; swap(l1,r1); r1--, r2--; ed1=Ed[r1]; ed2=Ed[r2]; flag=0; ed1 = lca(ed1, ed2); if(flag==1) { cout<<0; continue; } if(len[ed1]<=Len) { cout<<-len[ed1]<<endl; continue; } for(int j=20;j>=0;j--) { // cout<<"ed1: "<<fa[ed1][j]<<endl; if(len[fa[ed1][j]]>Len) { ed1=fa[ed1][j]; } } cout<<-len[fa[ed1][0]]<<endl; } }