2021杭电多校第三场题解

C

看到通配符首先想到FFT。统计S与T的匹配数fi,首先假设没有通配符,枚举0~9的每个数c,若s[i]=t[j]=c,则f[i-j]++。根据容斥原理,S的一个子串通过通配符与T匹配的方案数=S对应字符是通配符的数量+T对应字符是通配符的数量-二者对应字符都是通配符的数量。二者皆可通过FFT求出,时间复杂度O(11nlogn)

#include<bits/stdc++.h>
using namespace std;
const double pi=acos(-1.0);
const int N=2e6+7;
int n,m,nn,st,ss[N],R[N],f[N],ans[N];
char s[N],t[N];
struct node{
    double x,y;
    node(double xx=0,double yy=0){x=xx,y=yy;}
    node operator*(node a){return node(x*a.x-y*a.y,x*a.y+y*a.x);}
    node operator+(node a){return node(x+a.x,y+a.y);}
    node operator-(node a){return node(x-a.x,y-a.y);}
}a[N],b[N],p[N];
void fft(node *A,double f)
{
    for(int i=0;i<nn;i++)if(i<R[i])swap(A[i],A[R[i]]);
    for(int i=1;i<nn;i<<=1)
    {
        node wn(cos(pi/i),f*sin(pi/i));
        for(int j=0;j<nn;j+=i<<1)
        {
            node w(1,0);
            for(int k=0;k<i;k++,w=w*wn)
            {
                node x=A[j+k],y=w*A[j+i+k];
                A[j+k]=x+y,A[j+i+k]=x-y;
            }
        }
    }
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        scanf("%s%s",s,t);
        reverse(t,t+m);
        int len=0;
        nn=1;while(nn<=m+n-2)nn<<=1,len++;
        for(int i=0;i<=nn;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(len-1));
        for(int i=0;i<=nn;i++)p[i]=node(0,0),ans[i]=f[i]=0;
        for(int c=0;c<=9;c++)
        {
            for(int i=0;i<=nn;i++)a[i]=node((i<n&&s[i]-'0'==c)?1:0,0);
            for(int i=0;i<=nn;i++)b[i]=node((i<m&&t[i]-'0'==c)?1:0,0);
            fft(a,1),fft(b,1);
            for(int i=0;i<=nn;i++)p[i]=p[i]+a[i]*b[i];
        }
        fft(p,-1);
        for(int i=0;i<=nn;i++)f[i]+=(int)(p[i].x/nn+0.5);
        for(int i=0;i<n;i++)ss[i+1]=0;
        st=0;
        for(int i=0;i<n;i++)ss[i+1]=ss[i]+(s[i]=='*');
        for(int i=0;i<m;i++)st+=t[i]=='*';
        for(int i=0;i<=nn;i++)p[i]=node(0,0);
        for(int i=0;i<=nn;i++)a[i]=node((i<n&&s[i]=='*')?1:0,0);
        for(int i=0;i<=nn;i++)b[i]=node((i<m&&t[i]=='*')?1:0,0);
        fft(a,1),fft(b,1);
        for(int i=0;i<=nn;i++)p[i]=a[i]*b[i];
        fft(p,-1);
        for(int i=0;i<=nn;i++)f[i]-=(int)(p[i].x/nn+0.5);
        for(int i=m-1;i<n;i++)ans[m-(f[i]+ss[i+1]-ss[i-m+1]+st)]++;
        for(int i=1;i<=m;i++)ans[i]+=ans[i-1];
        for(int i=0;i<=m;i++)printf("%d
",ans[i]);
    }
}
View Code

D

签到。统计每种斜率出现次数。Bob的最优策略是避开斜率次数出现多的直线,Alice的最优策略是最小化斜率出现次数的最大值,即每次从每种斜率直线中各选一条

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7;
pair<int,int>a[N];
int n,ans[N];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        {
            ans[i]=0;
            int x1,y1,x2,y2,dx,dy,d;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            dx=x1-x2,dy=y1-y2;
            if(!dx)dy=1;else if(!dy)dx=1;
            else{
                if(dx<0)dx*=-1,dy*=-1;
                d=__gcd(abs(dx),abs(dy));
                dx/=d,dy/=d;
            }
            a[i]=make_pair(dx,dy);
        }
        sort(a+1,a+n+1);
        for(int i=1,j;i<=n;i=j)
        {
            j=i;while(j<=n&&a[i]==a[j])++j;
            for(int k=1;k<=j-i;++k)++ans[k];
        }
        for(int i=1,j=1;i<=n;++i)
        {
            while(!ans[j])++j;
            --ans[j];
            printf("%d
",i-j);
        }
    }
}
View Code

G

懒得写了,很没意思的一道签到题,纯粹考虑阅读能力。

贡献对255取min维护前缀和,时间复杂度O(n+q)

K

签到题,对区间长度记忆化搜索即可,代码略

持续更新中……

原文地址:https://www.cnblogs.com/hfctf0210/p/15110391.html