【NOIP2013】day1

来源:互联网 发布:蔚来汽车发展前景知乎 编辑:程序博客网 时间:2024/06/05 14:44

第一题:转圈游戏 http://codevs.cn/problem/3285/

本题的答案就是Ans = (x+m*10^k) mod n 这个没什么好说的,只是算法要用快速幂。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;int n,m,k,x;typedef long long ll;ll mul(int x,int p){ll tmp = x;ll ret = 1;while(p){if(p&1)ret = (ret*tmp)%n;tmp *= tmp;ret %= n;tmp %= n;p>>=1;}return ret%n;}int main(void){ios::sync_with_stdio(false);cin>>n>>m>>k>>x;cout<<(x+m*mul(10,k))%n;return 0;}/*Ans = (x+m*10^k) mod n*/
第二题:火柴排队http://codevs.cn/problem/3286/

这道题很容易想到贪心,就是a数组中第一小对应b数组第一小。。类推。。

逆序对:

设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。
如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。

然后是怎么想到逆序对的呢?



蓝色是他们之间的相对大小。 我们发现在上图中,要使得1-1,2-2,3-3,4-4配对,刚好是交换2次(b中第二个和第4个交换,然后再和第三个交换)而这个交换的次数,正好是图中的逆序对的数目,于是我们便发现逆序对就是答案。

求逆序对有多种方式,这里给出归并的求法。

归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来。

在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,并不产生逆序数;当a[i]>a[j]时,在

前半部分比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并

序中的合并过程中计算逆序数.


#define mod 99999997#define maxn 100010#include<cstdio>#include<iostream>#include<cmath>#include<algorithm>#include<cstring>using namespace std;int n,ans;struct pr{int v;int id;}a[maxn],b[maxn];bool cmp(pr _a,pr _b){return _a.v<_b.v;}int table[maxn],tmp[maxn];void marge(int l,int mid,int r){int i=l,j=mid+1,k;for(int k=l;k<=r;k++){if((i<=mid)&&(j>r||table[i]<=table[j]))tmp[k]=table[i++];else{tmp[k]=table[j++];ans+=mid-i+1;ans%=mod;}}for(int i=l;i<=r;i++)table[i]=tmp[i];}void margef(int l,int r){if(l<r){int mid = (l+r)>>1;margef(l,mid);margef(mid+1,r);marge(l,mid,r);}}int main(void){scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i].v),a[i].id=i;for(int i=1;i<=n;i++)scanf("%d",&b[i].v),b[i].id=i;sort(a+1,a+1+n,cmp);sort(b+1,b+1+n,cmp);for(int i=1;i<=n;i++)table[a[i].id]=b[i].id;/*求逆序对*/margef(1,n);printf("%d",ans%mod); return 0;}







1 0
原创粉丝点击