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

 

posted @ 2022-03-18 18:40  Authentic_k  阅读(29)  评论(0编辑  收藏  举报