zoukankan      html  css  js  c++  java
  • 20181015 考试记录&数论

    题目传送门

    W神爷的题解

    数论

    小 M 的算式

    【问题描述】

    小 M 在做数学作业的时候遇到了一个有趣的问题:有一个长度为 n 的数字

    串 S,小 M 需要在数字之间填入若干个“+”和恰好一个“=”,使其成为一个

    合法的等式。如对于 S=“2349”,可以通过添加 2个“+”和 1 个“=”成为

    “2+3+4=9”。

    小 M 发现有些数字串是无法通过添加符号成为一个合法的等式的,她想知

    道对于每一个给定的数字串 S,是否可以通过添加符号使之成为一个合法的等

    式(允许前导 0)?

    【输入】

    第一行为数据组数 T,表示有 T组输入数据。

    接下来 T行每行一个数字串 S。

    【输出】

    对于每组数据,若 S可以成为合法的等式,输出“Yes”,否则输出

    “No”,以单行回车隔开。

    【输入输出样例】

    equation.in

    4

    2349

    233233

    122323

    2344322322

    equation.out

    Yes

    Yes

    No

    Yes

    【输入输出样例解释】

    2+3+4=9

    233=233

    2+34=4+3+2+2+3+22

    【数据范围】

    对于 50%的数据:1 ≤ T ≤ 3,1 ≤ n ≤ 4。

    对于 100%的数据:1 ≤ T ≤ 5,1 ≤ n ≤ 10。

    简单的搜索

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    inline long long read()
    {
        long long f=1,ans=0;char c;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return f*ans;
    }
    char str[210];
    long long t,len,n1,n2;
    long long cnt1[210][210],cnt2[210][210],s1,s2;
    bool ff;
    void dfs2(long long pos,long long ans,long long dq,long long dqs)
    {
        if(dqs+dq>s1) return;
        if(ff) return;
        if(pos==n2+1)
        {
            s2=dqs+dq;
            if(s1==s2) 
                ff=1;
            return;
        }
        cnt2[ans][++cnt2[ans][0]]=str[pos]-'0';
        dq=dq*10+str[pos]-'0';
        dfs2(pos+1,ans,dq,dqs);
        dq=(dq-(str[pos]-'0'))/10;
        cnt2[ans][0]--;
        
        long long t=dq;
        cnt2[ans+1][++cnt2[ans+1][0]]=str[pos]-'0';
        dq=str[pos]-'0';
        dfs2(pos+1,ans+1,dq,dqs+t);
        cnt2[ans+1][0]--;
        dq=0;
        return;
    }
    void dfs1(long long pos,long long ans,long long dq,long long dqs)
    {
         if(ff) return;
        if(pos==n1+1)
        {
            s1=dqs+dq;
            dfs2(n1+1,1,0,0);
            return;
        }
        cnt1[ans][++cnt1[ans][0]]=str[pos]-'0';
        dq=dq*10+str[pos]-'0';
        dfs1(pos+1,ans,dq,dqs);
        cnt1[ans][0]--;
        dq=(dq-(str[pos]-'0'))/10;
        
        long long t=dq;
        cnt1[ans+1][++cnt1[ans+1][0]]=str[pos]-'0';
        dq=str[pos]-'0';
        dfs1(pos+1,ans+1,dq,dqs+t);
        cnt1[ans+1][0]--;
        
        dq=t;
        return;
    } 
    int main()
    {
        t=read();
        while(t--)
        {
            ff=false;
            scanf("%s",str+1);
            len=strlen(str+1);
            for(long long i=1;i<len;i++)
            {
                n1=i,n2=len;
                dfs1(1,1,0,0);
                if(ff){cout<<"Yes"<<endl;break;}
            }
            if(!ff)cout<<"No"<<endl;
        }
    }
    View Code

    小R的调度

    【问题描述】

    小 R 是一名 OIer,她刚刚学习了逆序对的相关知识:对于一个长度为 n的

    序列 A,其逆序对数定义为满足 i < j 且 A[i] > A[j]的(i, j)对数。

    现在小 R 有一个长度为 n的 01序列 A,小 R每次可以交换 当前 A[i]与 A[j]

    的值,并需要付出 c[i] + c[j]的代价。在若干轮(可以为空)交换后,小 R的得

    分为最终 A序列的逆序对数减去她在交换中付出的代价之和。

    小 R 想让最后的得分尽量大,你能帮帮她吗?

    【输入】

    第一行为一个正整数 n。

    第二行为一个长度为 n的字符串,表示 01序列 A。

    第三行为 n个整数 c[i],表示交换的代价参数。

    【输出】

    输出最大得分。

    【输入输出样例】

    inverse.in 

    6

    101010

    1 1 1 1 1 1

    inverse.out

    7

    【样例解释】

    交换 A[2]和 A[5],付出 1 + 1 = 2 的代价。

    最终序列为 111000,逆序对数为 9,得分为 9 - 2 = 7。

    【数据范围】

    对于 100%的数据:1 ≤ n ≤ 2501,0 ≤ c[i] ≤ 100。

    这是w神爷的题解:

    注意到每次交换只有当两位置的元素不同才有意义,进一步分析可知序列的每个位置最多被交换一次,而每个从 0 换成 1 的位置与每个从 1 换成 0 的位置是一一对应的。

    我们可以设计一个动态规划算法,设 f(i,j,k) 表示序列的前 i 个位置已经确定下来,前 i 个位置中有 j 个位置从 0 交换成了 1,有 k个位置从 1 交换成了 0。

    转移时枚举第 i + 1 个位置有没有发生交换,根据初始 A 1 到 A i有多少个 1,以及 j、k 的值可以计算出第 i+1 个位置确定为 0 产生的逆序对贡献。

    简称就是一个dp,可以想到转移为从0->1 与 1->0 

    然后就瞎搞

    但是为什么可以dp呢

    因为它不需要管后面发生了什么

    最后维护的是差值

    有加有减才能出来最后答案

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    inline int read(){
        int f=1,ans=0;char c;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return f*ans;
    } 
    int n,dp[2502][5005],cur,co,A[2502],inf=2<<30-1;
    char str[2512];
    int main(){
        n=read();
        scanf("%s",str+1);
        for(int i=1;i<=n;i++) A[i]=read();
        memset(dp,-127,sizeof(dp));
        dp[0][n]=0;
        for(int i=1;i<=n;i++){
            cur^=1;
            for(int j=0;j<=2*n;j++){
                int ans=dp[cur^1][j];
                if(ans==-inf) continue;
                if(str[i]=='1'){
                    dp[cur][j]=max(dp[cur][j],ans);
                    dp[cur][j-1]=max(dp[cur][j-1],ans+co-A[i]+j-n);
                }
                if(str[i]=='0'){
                    dp[cur][j]=max(dp[cur][j],ans+co+j-n);
                    dp[cur][j+1]=max(dp[cur][j+1],ans-A[i]);
                }
            }
            if(str[i]=='1') co++;
        }
        cout<<dp[cur][n];
    }
    View Code

    小 C  的机器人

    【问题描述】

    小 C 的机器人工厂里有 m个机器人,标号为 1 到 m号。给定一棵 n 个节点

    的有根树,根结点为 1号点,第 i个机器人初始在第 p[i]号点。树上每个节点有

    一盏灯,初始灯都是灭着的。树上每条边的距离均为 1,且机器人每次移动的

    距离均为 1。

    你是小 C 工厂的技术顾问,需要写一个程序模拟 q次操作,操作共 3种类

    型:

    操作 1:1 l r x,表示将标号在 l 和 r之间的所有机器人移动到 x号节点。

    操作 2:2 l r x,表示将标号在 l 和 r之间的所有机器人向根结点移动 x步。

    若移动步数超过机器人到根节点的距离,则机器人会停在根节点。

    操作 3:3 x,表示将 x号机器人当前所在节点的灯的亮灭状态取反,并查

    询当前所在节点到其子树内所有亮着灯的节点的距离之和。

    【输入】

    第一行为两个正整数 n,m。

    第二行至第 n行每行两个正整数 u、v,表示一条从 u到 v 的树边。

    第 n+1 行为 m 个正整数,第 i 个正整数表示 p[i]。

    第 n+2 行为一个正整数 q。

    接下来 q行表示 q 个操作,具体格式见样例。

    【输出】

    输出若干行,第 i行的整数表示第 i 个操作 3的答案。

    【输入输出样例】

    robot.in

    5 4

    1 2

    1 3

    3 4

    3 5

    1 1 1 1

    8

    1 2 2 4

    1 3 3 5

    3 3

    3 2

    2 2 2 1

    3 2

    3 3

    3 2

    robot.out

    0

    0

    2

    0

    1

    【数据范围】

    对于 20%的数据满足:1 ≤ n、m、q ≤ 10。

    另有 20%的数据满足:1 ≤ n ≤ 50。

    另有 20%的数据满足:1 ≤ m ≤ 20。

    对于 100%的数据满足:1 ≤ n、m、q ≤ 152501,1 ≤ u、v、p[i]、x ≤ n,1 ≤ l≤ r ≤ m

    毒瘤题目,到最后也没有写完,还算错复杂度

    将这个问题分解,不难想到其实就是两个子问题

    机器人的移动与子树距离

    第二个线段树用一下树链剖分的思想,十分简单

    想一下dfs在树上的时间戳

    若到达第i点的时间戳为k,并且它的子树大小为s,所以它在线段树中所

    所以建两个线段树去维护,lca去加速往上跳

    悲催的调试:第二个线段树写错,第一个线段树写错,lca写错

    最后就完了

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    inline int read()
    {
        int f=1,ans=0;char c;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return ans*f;
    }
    
    int p[312501],head[312501],tt[312501];
    int cnt,deep[312501],fa[312501][21],pd;
    int n,m,pig,size[312501],seg[312501];
    int rev[312501*4],ans[312501*4],q;
    
    struct node{
        int u,v,nex;
    }x[312501];
    
    void dfs(int f,int fath)
    {
        size[f]=1;
        tt[f]=++pig;
        seg[pig]=f;
        deep[f]=deep[fath]+1;
        fa[f][0]=fath;
        for(int i=1;(1<<i)<=deep[f];i++) fa[f][i]=fa[fa[f][i-1]][i-1];
        for(int i=head[f];i!=-1;i=x[i].nex)
        {
            if(x[i].v==fath) continue;
            dfs(x[i].v,f);
            size[f]+=size[x[i].v];
        }
        return;
    }
    
    int jump(int u,int h)
    {
        for(int i=20;i>=0;i--)
            if(h-(1<<i)>=0) u=fa[u][i],h-=(1<<i);
        if(u==0) return 1;
        return u;
    }
    
    void add(int u,int v)
    {
        x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++;
    }
    
    void build_seg(int k,int l,int r)
    {
        if(l==r){
            rev[k]=p[l];return;
        }
        int mid=l+r>>1;
        build_seg(k<<1,l,mid);
        build_seg(k<<1|1,mid+1,r);
        return;
    }
    void pushdown_seg(int k,int l,int r)
    {
        if(rev[k]!=0)
        {
            ans[k<<1]=ans[k<<1|1]=0;
            rev[k<<1]=rev[k<<1|1]=rev[k];
            rev[k]=0;
        }
        if(ans[k]!=0)
        {
            ans[k<<1]+=ans[k];
            ans[k<<1|1]+=ans[k];
            ans[k]=0;
        }
        return;
    }
    void change_seg(int k,int l,int r,int x,int y,int s)
    {
        
        if(x<=l&&r<=y){
            if(pd==1){
                rev[k]=s;
                ans[k]=0;
            }
            else if(pd==2){
                ans[k]+=s;
            }
            return;
        }
        pushdown_seg(k,l,r);
        int mid=l+r>>1;
        if(x<=mid) change_seg(k<<1,l,mid,x,y,s);
        if(mid<y) change_seg(k<<1|1,mid+1,r,x,y,s);
        return;
    }
    
    int query_seg(int k,int l,int r,int x,int y)
    {
        if(x==l&&r==y){return jump(rev[k],ans[k]);}
        pushdown_seg(k,l,r);
        int mid=l+r>>1;
        if(x<=mid) return query_seg(k<<1,l,mid,x,y);
        if(mid<y) return query_seg(k<<1|1,mid+1,r,x,y);
    }
    int sum[312501*4];
    int flag[312501*4];
    void change(int k,int l,int r,int x,int y)
    {
        if(l==x&&r==y){
            if(flag[k]==1) {
                flag[k]=0;
                sum[k]=0;
            }else if(flag[k]==0){
                flag[k]=1;
                sum[k]=deep[seg[l]]-1;
            }
            
            return;
        }
        int mid=l+r>>1;
        if(x<=mid) change(k<<1,l,mid,x,y);
        if(mid<y) change(k<<1|1,mid+1,r,x,y);
        sum[k]=sum[k<<1|1]+sum[k<<1];
        flag[k]=flag[k<<1]+flag[k<<1|1];
        return;
    }
    int ff;
    
    int query(int k,int l,int r,int x,int y)
    {
        
        if(x<=l&&r<=y){
            ff+=flag[k];
            return sum[k];
        }
        int mid=l+r>>1,st=0;
        if(x<=mid) st+=query(k<<1,l,mid,x,y);
        if(mid<y) st+=query(k<<1|1,mid+1,r,x,y);
        return st;
    }
    
    int main()
    {
        memset(head,-1,sizeof(head));
        n=read(),m=read();
        for(int i=1;i<n;i++) 
        {
            int u=read(),v=read();
            add(u,v),add(v,u);
        }
        dfs(1,0);
        for(int i=1;i<=m;i++) p[i]=read();
        build_seg(1,1,m);
        q=read();
        while(q--)
        {
            pd=read();
            if(pd==1||pd==2){
                int l=read(),r=read(),xx=read();
                change_seg(1,1,m,l,r,xx);
            }
            if(pd==3)
            {
                int st=read();
                int pos=query_seg(1,1,m,st,st);
                ff=0;
                int kk=query(1,1,n,tt[pos]+1,tt[pos]+size[pos]-1);
                change(1,1,n,tt[pos],tt[pos]);
                printf("%d
    ",kk-ff*(deep[pos]-1));
            }
        }
    }
    View Code
  • 相关阅读:
    js命名空间笔记
    css3兼容性问题归纳
    flexbox-CSS3弹性盒模型flexbox完整版教程
    JavaScript 预解析
    消除页面上的链接虚线框
    图片压缩之 PNG
    常见的前端优化技巧有哪些
    for-of循环和for-in循环的区别
    函数式编程初探
    js中同步与异步处理方法
  • 原文地址:https://www.cnblogs.com/si-rui-yang/p/9794700.html
Copyright © 2011-2022 走看看