zoukankan      html  css  js  c++  java
  • 2018 Multi-University Training Contest 2 部分简单题解析

    Preface

    多校第二场,依靠罚时优势打到了校内的Rank 2

    暴力分块碾标算系列


    T4 Game

    题目大意:在一个数集([1,n])中两个人轮流选择其中的一个数,并从数集中删去这个数所有约数。先将所有数删去的人获胜。

    比赛的时候手玩了(n<=5)的情况发现TM的怎么总是先手赢啊,然后就认为这是个先手必胜的游戏然而真的是这样

    赛后听法老讲了真正的做法,分假定情况讨论:

    • 假设这个状态先手必胜,那就让先手胜。
    • 假设这个状态后手必胜,那么先手把(1)取走之后相当与把这个必败态扔给了后手(因为(1)对后面的选取没有任何影响)

    故先手必胜,CODE

    #include<cstdio>
    using namespace std;
    int n;
    int main()
    {
        while (scanf("%d",&n)!=EOF) puts("Yes");
        return 0;
    }
    

    T10 Swaps and Inversions

    题目大意:给出一个数列,你可以花费(x)的代价交换两个数。在交换结束之后,还要花费(y*逆序对个数)的代价。问最小的代价。

    这个一个结论:交换次数等于逆序对个数,因此树状数组逆序对之后乘上(min(x,y))即可。

    CODE

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N=100005;
    int a[N],bit[N],b[N],n,m,x,y;
    long long ans;
    inline int find(int x)
    {
        int l=1,r=m,mid;
        while (l<=r)
        {
            mid=l+r>>1; if (b[mid]==x) return mid;
            if (b[mid]<x) l=mid+1; else r=mid-1;
        }
    }
    inline int lowbit(int x)
    {
        return x&-x;
    }
    inline int get(int x)
    {
        int res=0; for (;x<=m;x+=lowbit(x)) res+=bit[x]; return res;
    }
    inline void add(int x)
    {
        for (;x;x-=lowbit(x)) ++bit[x]; 
    }
    int main()
    {
        register int i;
        while (scanf("%d%d%d",&n,&x,&y)!=EOF)
        {
            for (i=1;i<=n;++i) scanf("%d",&a[i]),b[i]=a[i];
            memset(bit,0,sizeof(bit));
            sort(b+1,b+n+1); m=unique(b+1,b+n+1)-b-1;
            for (ans=0,i=1;i<=n;++i) 
            {
                a[i]=find(a[i]); ans+=get(a[i]+1); add(a[i]);
            }
            printf("%lld
    ",ans*(x<y?x:y));
        }
        return 0;
    }
    

    T7 Naive Operations

    题目大意:这题不用翻译也能看懂吧,毕竟都是常见套路。给你两个数组(a,b),初始时(a)的所有元素都为(0)。然后定义两种操作:

    • (add l r):将(a_i,iin[l,r])都加一
    • (query l r):询问(sum_{i=l}^r lfloor frac{a_i}{b_i} floor)

    官方给出的解法是搞两棵线段树,然后一波操作反正我不会

    比赛的时候YY了一个延时修改的分块,2000+ms卡过。

    首先我们记录一个块内的答案,然后考虑什么时候这个值才会被修改。

    当然是有个数(i)(或多个)的值被累加到多出一个(b_i)来了。

    有了这个思想,我们再维护一下每一块内最少还要整块累加多少次就会使答案发生改变,记作(v_i)

    然后修改的时候两端还是暴力改,整块的话也弄一个标记。

    然后核心的来了,我们修改的时候不更新整块标记,而是查询的时候

    查询还是先暴力计算两端,然后对于被查询的块,看一下整块的累加是否已经超过(v_i),是的话再更新。

    一般情况下速度良好,大致(O(nsqrt ncdot k))(k)为常数,大致在([In^2 n,In n])吧,其实主要还是和(b_i)的关系比较大。

    块乐的CODE

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<cmath>
    #include<cstring>
    using namespace std;
    typedef long long LL;
    const int N=100005,BLO=320;
    int n,m,a[N],b[N],l,r,blk[N],v[BLO],t[BLO],size;
    LL sum[BLO]; string s;
    inline int min(int a,int b)
    {
        return a<b?a:b;
    }
    inline void reset(int id)
    {
        register int i; v[id]=1e9;
        for (i=(id-1)*size+1;i<=id*size;++i)
        {
            sum[id]-=a[i]/b[i]; a[i]+=t[id];
            sum[id]+=a[i]/b[i]; v[id]=min(v[id],b[i]-a[i]%b[i]);
        } t[id]=0;
    }
    inline void modify(int l,int r)
    {
        register int i; 
        for (i=l;i<=min(blk[l]*size,r);++i) 
        {
            if (!(++a[i]%b[i])) ++sum[blk[l]]; v[blk[l]]=min(v[blk[l]],b[i]-a[i]%b[i]);
        }
        if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) 
        {
            if (!(++a[i]%b[i])) ++sum[blk[r]]; v[blk[r]]=min(v[blk[r]],b[i]-a[i]%b[i]);
        }
        for (i=blk[l]+1;i<=blk[r]-1;++i) ++t[i];
    }
    inline LL query(int l,int r)
    {
        register int i; LL tot=0;
        for (i=l;i<=min(blk[l]*size,r);++i) 
        tot+=(a[i]+t[blk[l]])/b[i];
        if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) tot+=(a[i]+t[blk[r]])/b[i];
        for (i=blk[l]+1;i<=blk[r]-1;++i)
        {
            if (t[i]>=v[i]) reset(i); tot+=sum[i];
        }
        return tot;
    }
    int main()
    {
        //freopen("7.in","r",stdin); freopen("7.out","w",stdout);
        ios::sync_with_stdio(false); register int i;
        while (cin>>n>>m)
        {
            memset(a,0,sizeof(a)); memset(v,63,sizeof(v)); 
            memset(t,0,sizeof(t)); memset(sum,0,sizeof(sum));
            for (size=sqrt(n),i=1;i<=n;++i) 
            cin>>b[i],blk[i]=(i-1)/size+1,v[blk[i]]=min(v[blk[i]],b[i]);
            while (m--)
            {
                cin>>s; cin>>l>>r; 
                if (s[0]=='a') modify(l,r); else cout<<query(l,r)<<endl;
            }
        }
        return 0;
    }
    

    Postscript

    主要是其他队伍要么没写出T7要么想线段树花了一段时间。

    不过还是很高兴的,出题人用心出题目,用脚造数据

  • 相关阅读:
    easyui 如何引入
    图片切换展示效果
    渐变弹出层
    C# GEP基因化编程
    C#操作内存
    移动的彩虹
    收缩和展开效果
    用SQL语句,删除掉重复项只保留一条
    图片切换,带标题文字
    Sql Server快速建表
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9671298.html
Copyright © 2011-2022 走看看