2018hdu多校第二场(hdu6312, hdu 6318, hdu 6315)

hdu 6312——Game

题意:给一个数字n,就代表有一个1~n的序列,A和B两个人分别可以对这个序列进行操作,每次操作可以删去一个数及这个数所有的因子,轮到谁时谁无法再进行操作谁就输了,也就是刚好删除最后一个数的人赢。A先走,问A能不能赢。

一看就能知道这是一道博弈论的题,先分析一下这个规则,从1-n,我们可以先不看1,因为删除任何一个数都可以顺便把1删除了,那么只从2~n中选择就一定有一个胜者,如果这个状态A胜的话,我们就可以选择那个让我们胜的数,如果A输,我们可以先去掉1来转变状态,这样A还是会胜。所以A是必胜的,只用输出YES就行了。

附上代码(注意多组输入):

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

hdu 6318——Swaps and Inversions

题意:给一串数字序列,如果一个数 a[i] 的逆序数 n[i] 不为0,那么要付出 n[i]*x 的代价,但是也可以将它与相邻位置的数交换减少它的逆序数,交换一次的代价是y,问至少要付出的代价是多少。

首先要知道什么是逆序数,逆序数的概念是,给定一个序列{An},对于An中的任何一个元素Ai(1≤i≤n),其前面i-1个元素比Ai大的元素个数j的总和Σj为该序列的逆序数。其次我们要明白,把一个逆序数不为0的序列中的数要交换到所有正确的位置使这个序列的逆序数为0的,交换的次数就等于这个序列的逆序数。这么说有点抽象,我们可以模拟一下:

有一个序列: 4 2 3 1 2

逆序数为    : 0 1 1 3 2

诶。。。好像在电脑上模拟这个交换很麻烦,麻烦自己手推一下。

所以我们只用判断x和y谁小,就用谁。然后关键就是求出逆序数,然后用逆序数乘上x和y之中比较小的那个数,求逆序数可以有很多方法,这里附上树状数组+离散化的代码:

#include
using namespace std;
typedef long long LL;
const int maxn=1e5+5;
int a[maxn],b[maxn],c[maxn];
int n;
LL x,y;
inline void add(int pos)
{
    while(pos<=n){
        c[pos]++;
        pos+=pos&(-pos);
    }
}
inline int getsum(int pos)
{
    int ret=0;
    while(pos>0){
        ret+=c[pos];
        pos-=pos&(-pos);
    }
    return ret;
}
int main()
{
    while(scanf("%d%lld%lld",&n,&x,&y)!=EOF){
        for(int i=1;i<=n;++i) scanf("%d",&a[i]),b[i]=a[i];
        sort(b+1,b+n+1);
        int tot=1;
        for(int i=2;i<=n;i++){
            if(b[i]!=b[i-1]) b[++tot]=b[i];
        }
        for(int i=1;i<=n;i++)
          a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
        LL sum=0;
        for(int i=n;i>=1;i--){
            sum+=1LL*getsum(a[i]-1);
            add(a[i]);
        }
        if(x>=y) 
           sum=sum*y;
        else sum=sum*x;
        printf("%lld\n",sum);
        for(int i=0;i<=n;i++)
         c[i]=0;
    }
    return 0;
}

 

hdu 6315——Naive Operations

题意:有两个数字序列An和Bn,An里的元素一开始全为0,Bn数组由题目给出,现在有两种操作

add l r 表示在An数组区间( l , r )中的每一个数都加1,

query l r 表示查询区间( l , r )的ai/bi的和。

因为涉及到区间更新区间和区间求和,所以考虑线段树,当然如果单纯的用线段树一点一点更新肯定复杂度太高,所以在这里我们可以利用这道题的特性,只有当 ai 加1的次数能够整除 bi 时才会对区间答案有贡献,这样用线段树记录一开始每个点初始化为bi,每次访问的区间有这个点就减1,当为0时表示可以整除bi此位置的贡献加1,然后向下查找初始的bi的值重新赋值,而当查询区间的和时,加上每个点的贡献就行了。大体思路就这样,贴一下标程题解。

附上代码:

#include
using namespace std;
const int maxn=100010;
struct node
{
    int l,r;
    int num;//记录当前位置的状态,一开始为b的值,为0时表示当前可以被整除
    int sum;//记录当前点整除的结果
    int lazy;//懒惰标记
    int b;//记录b数组的初始值
} a[maxn*4];
void buildtree(int l,int r,int rt)//建树
{
    a[rt].l=l;
    a[rt].r=r;
    a[rt].sum=0;
    a[rt].lazy=0;
    if(l==r)//找到叶子节点
    {
        scanf("%d",&a[rt].b);
        a[rt].sum=0;
        a[rt].num=a[rt].b;
        return;
    }
    int mid=(l+r)/2;
    buildtree(l,mid,2*rt);
    buildtree(mid+1,r,2*rt+1);
    a[rt].num=min(a[2*rt].num,a[2*rt+1].num);//维护区间最小值
}
void pushdown(int rt)//向下更新延迟标记
{
    if(a[rt].lazy)
    {
        a[2*rt].lazy+=a[rt].lazy;
        a[2*rt+1].lazy+=a[rt].lazy;
        a[2*rt].num+=a[rt].lazy;
        a[2*rt+1].num+=a[rt].lazy;
        a[rt].lazy=0;
    }
}
void update(int l,int r,int rt)//向上更新
{
    if(a[rt].l==l&&a[rt].r==r)
    {
        a[rt].num--;
        if(a[rt].num)///当前区间没有可以整除的点
        {
            a[rt].lazy--;
            return;
        }
        else ///有可以整除的点
        {
            if(a[rt].l==a[rt].r)
            {
                a[rt].sum++;
                a[rt].num=a[rt].b;
                return;
            }
        }
    }
    pushdown(rt);
    int mid=(a[rt].l+a[rt].r)/2;
    if(r<=mid)
        update(l,r,2*rt);
    else if(l>mid)
        update(l,r,2*rt+1);
    else
    {
        update(l,mid,2*rt);
        update(mid+1,r,2*rt+1);
    }
    a[rt].num=min(a[2*rt].num,a[2*rt+1].num);//维护区间最小值
    a[rt].sum=a[2*rt].sum+a[2*rt+1].sum;
}
int query(int l,int r,int rt)
{
    if(a[rt].r==r&&a[rt].l==l)
    {
        return a[rt].sum;
    }
    int mid=(a[rt].l+a[rt].r)/2;
    if(r<=mid)
        return query(l,r,2*rt);
    else if(l>mid)
        return query(l,r,2*rt+1);
    else
    {
        return query(l,mid,2*rt)+query(mid+1,r,2*rt+1);
    }
}
int main()
{
    int n,m;
    int l,r;
    char s[10];
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        buildtree(1,n,1);
        for(int i=1; i<=m; i++)
        {
            scanf("%s%d%d",s,&l,&r);
            if(s[0]=='a')
            {
                update(l,r,1);
            }
            else
            {
                int ans=query(l,r,1);
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}

 

你可能感兴趣的:(多校)