zoukankan      html  css  js  c++  java
  • 国庆集训 day1(牛客)

    部分题解

    A

    题意:找一个最大的回文后缀。

    题解:随便hash一下。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 400005;
    
    struct hashMap{
        typedef unsigned long long ull;
    
        const ull base = 233;
    
        ull h[N], pw[N];
    
        void getHash(char* s){
            int n=strlen(s+1);
            pw[0]=1;h[0]=0;
            for(int i=1;i<=n;++i){
                h[i]=h[i-1]*base+s[i]-'a';
                pw[i]=pw[i-1]*base;
            }
        }
    
        ull getVal(int l, int r){
            return h[r]-h[l-1]*pw[r-l+1];
        }
    }hs, hs1;
    
    char s[N], s1[N];
    
    int n;
    
    int ans[2];
    
    int main(){
        cin>>n;
        cin>>s+1;
    
        for(int i=n;i>=1;--i){
            s1[n-i+1]=s[i];
        }
    
        hs.getHash(s);
        hs1.getHash(s1);
    
        int len=0;
        for(int i=n;i>=1;--i){
            int t=n-i+1;
            if(t>i-1)break;
    
            if(hs.getVal(i,n)==hs1.getVal(n-i+2,n-(i-t)+1)){
                len=t;
            }
        }
    
        ans[0]=n-2*len;
    
        len=0;
        for(int i=n;i>=1;--i){
            int t=n-i;
            if(t>i-1)break;
    
            if(hs.getVal(i+1,n)==hs1.getVal(n-i+2,n-i+t+1)){
                len=t;
            }
        }
    
        ans[1]=n-(2*len+1);
    
        cout<<min(ans[0],ans[1]);
    
        return 0;
    }
    
    

    B

    题意:枚举区间,求区间gcd*区间max的和。

    题解:考虑枚举最大值,这里采用笛卡尔树。原因是笛卡尔树采用大根堆结构的时候,我们能够轻易的知道,每一个最大值能够主导的区间。我们知道每一个最大值能够主导的区间之后,现在要考虑的是,这个区间内的gcd情况。首先我们要知道一个结论(比较直观),包含这个值,区间向左或者右延展的时候,区间gcd是递减的(非常直观),且取值最多有gcd个(每次至少少一个质因数)。我们只需要得到每一个gcd的值和区间长度(这个可以用st表二分查询)就可以得到答案。最终在O(nlogn^2)的时间就可以做完这道题。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 200005;
    
    const int MOD = 1e9+7;
    
    int a[N], n, ans;
    
    int st[N], top, ls[N], rs[N];
    
    inline int gcd(int a, int b){return b==0?a:gcd(b,a%b);}
    
    struct RMQ{
        int lg[N], mn[N][21];
    
        void build(){
            for(int i=1;i<=n;++i)mn[i][0]=a[i];
            for(int i=2;i<=n;++i)lg[i]=lg[i>>1]+1;
    
            for(int i=1;i<=20;++i){
                for(int j=1;j+(1<<i)-1<=n;++j){
                    mn[j][i]=gcd(mn[j][i-1],mn[j+(1<<(i-1))][i-1]);/*bug*/
                }
            }
        }
    
        int query(int l, int r){
            int len=lg[r-l+1];
            return gcd(mn[l][len],mn[r-(1<<len)+1][len]);
        }
    }table;
    
    int L[N], R[N], tL[N], tR[N];
    
    void solve(int nd, int l, int r){
        if(ls[nd])solve(ls[nd],l,nd-1);
        if(rs[nd])solve(rs[nd],nd+1,r);
    
        L[0]=R[0]=0;
        int x=nd, y=nd;
    
        while(x>=l){
            int g=table.query(x,nd);
            L[++L[0]]=g;
            int ll=l, rr=x, mid;
            int id=x;
    
            while(ll<=rr){
                mid=ll+rr>>1;
                if(table.query(mid,nd)==L[L[0]]){
                    id=mid;
                    rr=mid-1;
                }else{
                    ll=mid+1;
                }
            }
    
            tL[L[0]]=x-id+1;
            x=id-1;
        }
    
        while(y<=r){
            int g=table.query(nd,y);
            R[++R[0]]=g;
            int ll=y, rr=r, mid;
            int id=y;
    
            while(ll<=rr){
                mid=ll+rr>>1;
                if(table.query(nd,mid)==R[R[0]]){
                    id=mid;
                    ll=mid+1;
                }else{
                    rr=mid-1;
                }
            }
    
            tR[R[0]]=id-y+1;
            y=id+1;
        }
        int res=0;
        for(int i=1;i<=L[0];++i){
            for(int j=1;j<=R[0];++j){
                int g=gcd(L[i],R[j]);
                res+=1ll*g*tL[i]%MOD*tR[j]%MOD;
                res%=MOD;
            }
        }
    
        ans+=1ll*res*a[nd]%MOD;
        ans%=MOD;
    }
    
    int main(){
        ios_base::sync_with_stdio(0);
        cin.tie(0);
    
        cin>>n;
        for(int i=1;i<=n;++i){
            cin>>a[i];
        }
    
        table.build();
    
        for(int i=1;i<=n;++i){
            while(top&&a[st[top]]<a[i])ls[i]=st[top--];
            if(top)rs[st[top]]=i;
            st[++top]=i;
        }
    
        solve(st[1],1,n);
    
        cout<<ans;
    
        return 0;
    }
    

    G

    题意:你现在需要去组织一个长度为n的只包含小写字母的字符串,使得这个字符串中不能出现给定的q个禁忌字符串。

    题解:首先,我们将trie树建出来。然后可以想到,在匹配过程中,树上有一些节点是不合法的。这就是那些本身是结束节点,或者自己的后缀是结束节点(由于建立fail树的时候,是按照BFS序去建立的,所以可以直接对这些不合法的节点打上标记)。然后,我们可以将带有fail指针的trie树看成是一个有向图。这样,问题就能转化为:在一个有向图中,从0节点出发,走k步,到达某一个节点的方案数。这个可以通过矩阵的快速幂来解决。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 100005;
    
    const int MOD = 1e9+7;
    
    const int M = 26;
    
    struct trieNode{
        int son[M], mark, fail;
    }T[N];
    
    int cntNode;
    
    void Insert(char* s){
        int len=strlen(s+1);
        int cur=0;
    
        for(int i=1;i<=len;++i){
            if(!T[cur].son[s[i]-'a']){
                T[cur].son[s[i]-'a']=++cntNode;
            }
            cur=T[cur].son[s[i]-'a'];
        }
    
        T[cur].mark=1;
    }
    
    struct m{
        int a[105][105];
    
        m operator*(m m1){
            m ret;
            for(int i=0;i<=cntNode;++i){
                for(int j=0;j<=cntNode;++j){
                    ret.a[i][j]=0;
                    for(int k=0;k<=cntNode;++k){
                        ret.a[i][j]+=1ll*a[i][k]*m1.a[k][j]%MOD;
                        ret.a[i][j]%=MOD;
                    }
                }
            }
    
            return ret;
        }
    }gkd;
    
    m powmod(m m1, int k){
        m ret;
        memset(ret.a,0,sizeof ret.a);
    
        for(int i=0;i<=cntNode;++i)ret.a[i][i]=1;
    
        while(k){
            if(k&1)ret=ret*m1;
            m1=m1*m1;
            k>>=1;
        }
    
        return ret;
    }
    
    void getFail(){
        queue<int> q;
    
        for(int i=0;i<26;++i){
            int nd=T[0].son[i];
            if(nd){
                q.push(nd);
                T[nd].fail=0;
            }
        }
    
        while(!q.empty()){
            int nd=q.front();
            q.pop();
            if(T[T[nd].fail].mark>0)T[nd].mark=1;
            for(int i=0;i<26;++i){
                int& nd1=T[nd].son[i];
                if(!nd1){
                    nd1=T[T[nd].fail].son[i];
                }else{
                    T[nd1].fail=T[T[nd].fail].son[i];
                    q.push(nd1);
                }
            }
        }
    }
    
    void build(){
        for(int i=0;i<=cntNode;++i){
            for(int j=0;j<26;++j){
                int nd=T[i].son[j];
                if(T[i].mark==0&&T[nd].mark==0){
                    gkd.a[i][nd]++;
                }
            }
        }
    }
    
    char s[N];
    
    int n, q;
    
    int main(){
        ios_base::sync_with_stdio(0);
        cin.tie(0);
    
        cin>>n>>q;
    
        for(int i=1;i<=q;++i){
            int t;cin>>t;
            cin>>s+1;
            Insert(s);
        }
    
        getFail();
    
        build();
    
        m res=powmod(gkd,n);
    
        int ans=0;
        for(int i=0;i<=cntNode;++i){
            ans+=res.a[0][i];
            ans%=MOD;
        }
    
        cout<<ans;
    
        return 0;
    }
    

    D

    题意:给你一根过原点的直线和平面直角坐标系中n个点,你现在需要在直线上选择一个点,使得过这个点的一个固定大小直径的圆能覆盖最多的点。

    题解:首先过每一个点,作一个大小为R的圆。然后,我们需要的仅仅是原点到这两个点的有向线段长度而已。丢进一个容器里,排序后扫描线。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 300005;
    
    #define all(x) x.begin(), x.end()
    
    const double eps = 1e-3;
    
    struct P{
        double x, y;
    
        P(){}
        P(double _x, double _y){
            x=_x;
            y=_y;
        }
    }pts[N];
    
    struct Node{
        double x;
        int o;
    
        bool operator<(const Node& rhs)const{
            if(fabs(x-rhs.x)<1e-10)return o>rhs.o;
            else return x<rhs.x;
        }
    };
    
    double cross(P p1, P p2){
        return p1.x*p2.y-p1.y*p2.x;
    }
    
    double dot(P p1, P p2){
        return p1.x*p2.x+p1.y*p2.y;
    }
    
    int n;
    
    double r, a, b;
    
    int main(){
        ios_base::sync_with_stdio(0);
        cin.tie(0);
    
        cin>>n>>r>>a>>b;
        r+=eps;
        for(int i=1;i<=n;++i){
            double x,y;
            cin>>x>>y;
            pts[i]=(P){x,y};
        }
    
        P v1=P(a,b);
    
        vector<Node> points;
    
        double len=sqrt(a*a+b*b);
    
        if(len<eps){
            int cnt=0;
            for(int i=1;i<=n;++i){
                if(pts[i].x*pts[i].x+pts[i].y*pts[i].y<=r*r)++cnt;
            }
    
            cout<<cnt;
            return 0;
        }
    
        for(int i=1;i<=n;++i){
            P v2=P(pts[i].x, pts[i].y);
    
            double t1=dot(v1,v2)/len;
            double t2=cross(v1,v2)/len;
    
            if(fabs(t2)-r>0)continue;
    
            double delta=sqrt(r*r-t2*t2);
    
            double ll=t1-delta, rr=t1+delta;
    
            points.push_back((Node){ll,1});
            points.push_back((Node){rr,-1});
        }
    
        sort(all(points));
    
        int t=0;
        int res=0;
    
        for(auto& p:points){
            t+=p.o;
            res=max(res,t);
        }
    
        cout<<res;
    
        return 0;
    }
    

    F

    题意:n个数字,从中选取k个。使得它们的二进制按位与之后得到的结果最大。

    题解:首先,从高位到低位考虑的时候,如果某一位对应的数字个数超过k个,那剩下的就会被丢弃。然后问题就被转化为一个子问题了,模拟一下就能过。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    int vis[200005];
    
    int a[200005][32];
    
    int n, k;
    
    int main(){
        cin>>n>>k;
    
        for(int i=1;i<=n;++i){
            int x;cin>>x;
            for(int j=0;j<=29;++j){
                if(x>>j&1){
                    a[i][j]=1;
                }
            }
        }
    
        for(int i=29;i>=0;--i){
            int cnt=0;
            for(int j=1;j<=n;++j){
                if(!vis[j]&&a[j][i])++cnt;
            }
    
            if(cnt>=k){
                for(int j=1;j<=n;++j){
                    if(!vis[j]&&!a[j][i])vis[j]=1;
                }
            }
        }
    
        int res=-1;
        for(int i=1;i<=n;++i){
            if(!vis[i]){
                int t=0;
                for(int j=0;j<=29;++j){
                    if(a[i][j]){
                        t|=(1<<j);
                    }
                }
                res&=t;
            }
        }
    
        cout<<res;
    
        return 0;
    }
    
    
  • 相关阅读:
    python14 1.带参装饰器 | wrapper 了了解 # 2.迭代器 ***** # 可迭代对象 # 迭代器对象 # for迭代器 # 枚举对象
    python13 1.函数的嵌套定义 2.global、nonlocal关键字 3.闭包及闭包的运用场景 4.装饰器
    python12--字符串的比较 函数的默认值的细节 三元表达式 函数对象 名称空间 作用域 列表与字典的推导式 四则运算 函数的嵌套
    python11 函数的定义,调用,分类
    python10--函数的来源,优点,定义,组成,使用(定义,调用)函数的分类,函数的返回值
    python1--计算机原理 操作系统 进制 内存分布
    python2 配置环境变量
    python3 数据类型
    Java创建线程的三种方式
    webhooks动态更新配置
  • 原文地址:https://www.cnblogs.com/JohnRan/p/13783236.html
Copyright © 2011-2022 走看看