2018 Multi-University Training Contest 2 【杭电多校2】(分治排序求逆序对数目)

1004:Game

题意:两个人轮流从1~n的数列中选择数。每次选择一个数,就从数列中消除所有这个数的因子,最后轮到谁数字全被消除了(即数列为空了),谁就输了。问针对所给的n,先手的人是否能赢,输出Yes和No

思路:这是个先手必胜的题,emmmmm(亏我们推了那么久,还找规律)

代码:

#include<iostream>

using namespace std;

int main(){
    int n;
    while(cin>>n){
        cout<<"Yes"<<endl;

    }
}

1010:Swaps and Inversions

(分治排序求逆序对数目)

地址:http://acm.hdu.edu.cn/contests/contest_showproblem.php?cid=803&pid=1010

题意:两种选择,1:不调顺序,直接交存在逆序对的钱(按照每对为单位计算),2:交换两个数,但只能交换相邻的。(也要给钱)。告诉你两种选择分别需要的钱数,问花费最少的钱是多少

思路:因为只能交换相邻的,所以逆序对的数目等于如果要全部调成顺序的所需要交换的次数。思路很简单,重点是如何做到求逆序对数目而不超时。我们队临时学习了一波分治排序法(二分),复杂度(n*logn)。直接看代码吧。

ps:有一个很重要的TLE点!这个时间好像卡的很紧。卡输入输出,建议用scanf,还有就是我加了memset(temp,0,sizeof(temp));这句话也会超时,删后AC

代码:

#include <algorithm>
#include <cstring>
#include <cstdio>
#define N 100005
#define ll long long
using namespace std;
int an[N];
int temp[N];

//因为只能交换相邻的元素,所以逆序对的数目和交换的次数相等,故求出逆序对的数目,乘以min(交换的花费,逆序对的赔款费用)就是最终的答案 
ll num=0;//全局变量,逆序对的数目 

void merge(int start,int mid,int endd)//二分思想分治排序防止超时,复杂度(n*logn) 
//(默认两部分区间[start,mid][mid+1,endd]都排好了顺序),计算两个区间的逆序对数目并对该整个部分区间进行排序 
{
    //int *temp=new int [endd-start+1];//开辟一个临时数组,节省空间,并储存排好序后的数组
    //memset(temp,0,sizeof(temp));
    int k=0,i=start,j=mid+1; 
    while(i<=mid&&j<=endd)
    {
        if(an[i]<=an[j])
            temp[k++]=an[i++];
        else
        {
            temp[k++]=an[j++];
            num+=mid-i+1;
        }
            
    }
    //接下来为了得到完整的排序之后的数组,放入没有遍历到的数组 
    while(i<=mid)
    {
        temp[k++]=an[i++];    
    } 
    while(j<=endd)
    {
        temp[k++]=an[j++];
    }
    for(int i=0;i<k;++i)//用排好序的数组覆盖这部分区间 
    {
        an[start+i]=temp[i];
    }
    //memset(temp,0,sizeof(temp));
} 
void dfsmerge(int start,int endd)//用递归来对两部分的区间进行排序
{
    if(start<endd)
    {
        int mid=(start+endd)/2;
        
        dfsmerge(start,mid);//对左边区间进行排序 
        dfsmerge(mid+1,endd);//对右边区间进行排序 
        merge(start,mid,endd); //对左右两部分区间同时进行逆序对的计算 
    }
} 
int main()
{
    int n,x,y;
    while(scanf("%d%d%d",&n,&x,&y)!=EOF)
    {
        num=0;
        memset(an,0,sizeof(an));
        for(int i=0;i<n;++i)
            scanf("%d",&an[i]);
        dfsmerge(0,n-1);
        //cout<<num<<endl;//输出逆序对的数目
        
        printf("%lld\n",(ll)min(x,y)*num); 
    }
    return 0;
}

ps:逆序对的求法除了分治排序,还有树状数组等,可以上网搜搜相关博客。我是从这个博客学习分治算法的,(树状数组还没看……)板子很清晰,挺好理解。推荐一波:https://blog.youkuaiyun.com/u010128136/article/details/52852093

树桩数组和二叉树写法可以看看这个:https://blog.youkuaiyun.com/qq_37383726/article/details/76459527

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值