zoukankan      html  css  js  c++  java
  • HUAS 2017暑假第六周比赛-题解

    A.Parenthesis

    括号匹配的问题有一种经典的做法。
    将左括号看成1,右括号看成-1,做一遍前缀和sum。
    括号序列是合法的当且仅当(sum[n]=Min(sum[1],sum[2]....sum[n])=0)时成立。
    于是问题变成了交换两个括号后如何维护sum数组的值。
    实际上交换a和b之后只会影响到((sum[a],sum[a+1]....sum[b-1]))
    1.(s[a]=(,s[b]=)) 对应的区间减2。
    2.(s[a]=),s[b]=() 对应的区间加2。
    我们只需要维护一个支持区间查询最小值,区间加减的数据结构即可。
    显然线段树可以完成这些操作。时间复杂度(O(qlogn))

    # include <bits/stdc++.h>
    using namespace std;
    const int N=100005;
    
    int sum[N], seg[N<<3], tag[N<<3];
    char s[N];
    
    void push_up(int p){seg[p]=min(seg[p<<1],seg[p<<1|1]);}
    void push_down(int p){
        if (!tag[p]) return ;
        seg[p]+=tag[p]; tag[p<<1]+=tag[p]; tag[p<<1|1]+=tag[p]; tag[p]=0;
    }
    void init(int p, int l, int r){
        if (l<r) {
            int mid=(l+r)>>1;
            init(p<<1,l,mid); init(p<<1|1,mid+1,r); push_up(p);
        }
        else seg[p]=sum[l], tag[p]=0;
    }
    void update(int p, int l, int r, int L, int R, int val){
        push_down(p);
        if (L>r||R<l) return ;
        if (L<=l&&R>=r) tag[p]+=val, push_down(p);
        else {
            int mid=(l+r)>>1;
            update(p<<1,l,mid,L,R,val); update(p<<1|1,mid+1,r,L,R,val); push_up(p);
        }
    }
    int main ()
    {
        int n, q, l, r;
        while (~scanf("%d%d",&n,&q)) {
            scanf("%s",s+1);
            for (int i=1; i<=n; ++i) sum[i]=sum[i-1]+(s[i]=='('?1:-1);
            init(1,1,n);
            while (q--) {
                scanf("%d%d",&l,&r);
                if (l>r) swap(l,r);
                if (s[l]!=s[r]) {
                    if (s[l]=='(') update(1,1,n,l,r-1,-2);
                    else update(1,1,n,l,r-1,2);
                }
                puts(seg[1]==0?"Yes":"No");
                if (s[l]!=s[r]) {
                    if (s[l]=='(') update(1,1,n,l,r-1,2);
                    else update(1,1,n,l,r-1,-2);
                }
            }
        }
        return 0;
    }
    

    B.权势二进制

    题目可以换一种描述。
    给出n,问至少可以表示为多少个数位上面只有0和1的数字之和。
    显然答案即为n的十进制数位的最大值,即(max(n\%10,n\%100,n\%1000....))
    时间复杂度(O(logn))

    # include <bits/stdc++.h>
    using namespace std;
    
    char s[10];
    
    int main ()
    {
        int ans=0;
        scanf("%s",s);
        for (int i=0; s[i]; ++i) ans=max(ans,s[i]-'0');
        printf("%d
    ",ans);
        return 0;
    }
    

    C.天气晴朗的魔法

    如果不考虑边权的最大值最小的话,答案就是最大生成树。
    现在要求图的生成树中边权的最大值最小。
    实际上由Kruskal的算法流程可知,这个值就是图的最小生成树的最大边,不可能比这更小了。
    因此,先对图求一遍最小生成树,获得这个最大值,然后把小于等于这个最大值的边
    拉出来再求一遍最大生成树,显然即为答案。
    时间复杂度(O(mlogm))

    # include <bits/stdc++.h>
    using namespace std;
    const int N=200005;
    
    struct Edge{int u, v, w;}edge[N];
    int n, m, F[N];
    long long ans=0;
    
    int find(int x){return F[x]==0?x:F[x]=find(F[x]);}
    bool comp(Edge a, Edge b){return a.w<b.w;}
    void Kruskal(){
        sort(edge+1,edge+m+1,comp);
        int Max_w;
        for (int i=1; i<=m; ++i) {
            int u=find(edge[i].u), v=find(edge[i].v);
            if (u==v) continue;
            F[u]=v; Max_w=edge[i].w;
        }
        memset(F,0,sizeof(F));
        for (int i=m; i>=1; --i) {
            if (edge[i].w>Max_w) continue;
            int u=find(edge[i].u), v=find(edge[i].v);
            if (u==v) continue;
            F[u]=v; ans+=edge[i].w;
        }
    }
    int main ()
    {
        scanf("%d%d",&n,&m);
        for (int i=1; i<=m; ++i) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
        Kruskal();
        printf("%lld
    ",ans);
        return 0;
    }
    

    D.大鱼吃小鱼

    用栈来进行模拟,向右边走的鱼入栈,左边走的鱼和栈顶的鱼进行比较,
    如果左边走的鱼比栈顶的鱼体积小,那么就会被吃掉。
    否则吃掉栈顶的鱼继续和栈顶的下一个鱼进行比较,直到被吃掉或者栈为空为止。
    模拟过程中一共多少鱼会被吃掉,最后拿总数减去它即可答案。
    时间复杂度(O(n))

    # include <bits/stdc++.h>
    using namespace std;
    const int N=100005;
    
    int st[N], top=0;
    
    int main ()
    {
        int n, a, b, sum=0;
        scanf("%d",&n);
        for (int i=1; i<=n; ++i) {
            scanf("%d%d",&a,&b);
            if (b==1) st[++top]=a;
            else {
                while (top&&st[top]<a) --top, ++sum;
                if (top) ++sum;
            }
        }
        printf("%d
    ",n-sum);
        return 0;
    }
    

    E.0和1相等串

    将0写成-1,对原序列做一遍前缀和sum。
    那么区间([l,r])的0和1出现次数相等的充分必要条件为(sum[r]=sum[l-1])
    现在要找到最大的(r-l+1)使得(sum[r]=sum[l-1])
    我们扫一遍sum数组,记录每个val对应的(sum[i]=val)的下标i的最大值(i_{max})和最小值(i_{min})。那么这个val对应的答案就是(i_{max}-i_{min})
    对所有val的答案取最大值即可。
    时间复杂度(O(len))

    # include <bits/stdc++.h>
    using namespace std;
    const int N=1000005;
    
    int sum[N], Min_p[N<<1], Max_p[N<<1];
    char s[N];
    const int P=1000000;
    
    int main ()
    {
        scanf("%s",s+1);
        int n=strlen(s+1);
        memset(Min_p,-1,sizeof(Min_p)); memset(Max_p,-1,sizeof(Max_p));
        for (int i=1; i<=n; ++i) sum[i]=sum[i-1]+(s[i]=='1'?1:-1);
        for (int i=0; i<=n; ++i) {
            int x=sum[i];
            if (Min_p[x+P]==-1) Min_p[x+P]=i;
        }
        for (int i=n; i>=0; --i) {
            int x=sum[i];
            if (Max_p[x+P]==-1) Max_p[x+P]=i;
        }
        int ans=0;
        for (int i=0; i<=n+P; ++i) if (Min_p[i]!=-1) ans=max(ans,Max_p[i]-Min_p[i]);
        printf("%d
    ",ans);
        return 0;
    }
    

    F.部落划分

    如果我们已经确定了最优划分时,最近的两个部落的距离为d,那么我们至多可以将野人划分为几个部落呢?
    我们可以推出,如果两个野人之间的距离<d,那么它们必然是一个部落的。
    那么我们可以(O(n^2))枚举两个野人,将相同部落的野人的用并查集合并,最后可以得到野人至多被划分为几个部落。
    现在的问题是,我们还无法知道答案d应该为多少。
    幸运的是,令划分后的部落数(y=f(d)),这个函数是单调递减的。
    于是我们可以二分d,来找到最大的d,使得(f(d)=K)。此时即为答案。
    时间复杂度(O(n^2log(maxdis)))

    # include <bits/stdc++.h>
    using namespace std;
    const int N=100005;
    
    int a[N][2], f[N], n, k, F[N];
    
    int find(int x){return F[x]==0?x:F[x]=find(F[x]);}
    bool check(double x){
        int res=n, u, v;
        x=x*x;
        memset(F,0,sizeof(F));
        for (int i=1; i<=n; ++i) for (int j=i+1; j<=n; ++j) {
            if ((a[i][0]-a[j][0])*(a[i][0]-a[j][0])+(a[i][1]-a[j][1])*(a[i][1]-a[j][1])>=x) continue;
            u=find(i), v=find(j);
            if (u!=v) F[u]=v, --res;
        }
        return res>=k;
    }
    int main ()
    {
        scanf("%d%d",&n,&k);
        for (int i=1; i<=n; ++i) scanf("%d%d",&a[i][0],&a[i][1]);
        double l=0, r=20000, mid;
        for (int i=1; i<=50; ++i) {
            mid=(l+r)/2;
            if (check(mid)) l=mid;
            else r=mid;
        }
        printf("%.2f
    ",mid);
        return 0;
    }
    

    G.2016

    ((a imes b)\%2016=0Rightarrow ((a\%2016) imes (b\%2016))\%2016=0)
    (x=a\%2016,y=b\%2016Rightarrow 0<=x<2016,0<=y<2016)
    预处理出满足条件的((x,y)),对于每个这样的((x,y))求出对应的((a,b))有多少种即可。
    显然应为(frac{n-x}{2016} imes frac{m-y}{2016})。另外注意对x=0和y=0情况的特殊处理即可。
    时间复杂度(O(2016 imes 2016))

    # include <bits/stdc++.h>
    using namespace std;
    const int N=2015;
    
    struct Node{int x, y;}node[N*N];
    int pos;
    
    void init(){
        for (int i=0; i<=2015; ++i) for (int j=0; j<=2015; ++j) {
            if (i*j%2016) continue;
            node[++pos].x=i; node[pos].y=j;
        }
    }
    int main ()
    {
        init();
        int n, m;
        long long ans;
        while (~scanf("%d%d",&n,&m)) {
            ans=0;
            for (int i=1; i<=pos; ++i) {
                if (node[i].x>n || node[i].y>m) continue;
                ans+=(long long)((n-node[i].x)/2016+(node[i].x?1:0))*((m-node[i].y)/2016+(node[i].y?1:0));
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    flask-admin章节二:wtforms的使用以及在数据库场景中使用QuerySelectField代替SelectField
    flask-admin章节一:使用chartkick画报表
    flask-admin众博客概述
    python smtplib发送邮件遇到的认证问题
    python logging模块可能会令人困惑的地方
    Markdown
    SpringBoot-启动过程
    SpringBoot-目录
    AbstractQueuedSynchronizer
    ThreadLocal
  • 原文地址:https://www.cnblogs.com/lishiyao/p/7400609.html
Copyright © 2011-2022 走看看