NOIP2013提高组题解

\(D1T1\) 转圈游戏 \((OK)\)

\(D1T2\) 火柴排队 \((OK)\)

\(D1T3\) 货车运输 \((OK)\)

\(D2T1\) 积木大赛 \((OK)\)

\(D2T2\) 花匠 \((OK)\)

\(D2T3\) 华容道

出于对这年唯一一道紫题的尊重,我只会做\(5\)道,不过我觉得华容道这道题我之后肯定回来填坑的.

\(D1T1\)显然会有循环节的存在,于是我手玩了几组简单数据找循环节,\(T=\frac{n}{gcd(n,m)}\),所以就只要从\(x\)开始往后面模拟\((10^k \mod T)\)步即可.

然后去看了一下题解,给出的答案是\((x+m*10^k)\mod n\),更加简单了,但是我觉得我的更好理解,题解给出的答案应该是直接跳过了找循环节这一步骤的.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
inline ll ksm(ll a,int b,int c){
	ll cnt=1;
	while(b){
		if(b&1)cnt=(1ll*cnt*a)%c;
		a=(1ll*a*a)%c;
		b>>=1;
	}
	return cnt;
}
inline int gcd(int a,int b){
	if(!b)return a;
	return gcd(b,a%b);
}
int main(){
	int n=read(),m=read(),k=read(),x=read();
	int T=n/gcd(n,m),res=ksm(10,k,T);
	while(res--)x=(x+m)%n;printf("%d\n",x);
    return 0;
}

\(D1T2\)这道题我是问了\(DTTTTT\)的,首先肯定都知道两个序列最后肯定是按照大小一一配对.接下来我觉得要借助样例才能很好的解释.例如对于样例二:

A:1 3 4 2

B:1 7 2 4

把两个序列都离散化后得到:

A:1 3 4 2

B:1 4 2 3

然后因为同时移动两个序列的元素 和 只移动一个序列的元素 使这两个序列配对成功 产生的效果和答案都是一样的.所以我们考虑把\(B\)序列移动成\(A\)序列即可.所以相当于再对\(B\)序列进行一次离散化,得到新序列\(C[i]\)表示\(B[i]\)在序列\(A\)中的位置,即

C:1 3 4 2

现在相当于要通过每次交换相邻两项使得C序列变成一个单调递增的序列(这样就是\(AB\)两个序列一一配对的了),就是找逆序对了.树状数组和归并排序都可以.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int mod=99999997;
const int N=1e5+5;
int n,ans,a[N],b[N],c[N],d[N],e[N],t[N],pos[N];
inline void add(int x,int v){for(;x<=n;x+=x&-x)t[x]+=v;}
inline int ask(int x){int cnt=0;for(;x;x-=x&-x)cnt+=t[x];return cnt;}
int main(){
	n=read();
	for(int i=1;i<=n;++i)a[i]=read(),c[i]=a[i];
	for(int i=1;i<=n;++i)b[i]=read(),d[i]=b[i];
	sort(c+1,c+n+1);sort(d+1,d+n+1);
	for(int i=1;i<=n;++i){
		a[i]=lower_bound(c+1,c+n+1,a[i])-c;pos[a[i]]=i;
		b[i]=lower_bound(d+1,d+n+1,b[i])-d;
	}
	for(int i=1;i<=n;++i)e[i]=pos[b[i]];
	for(int i=1;i<=n;++i){
		int sum=ask(e[i]);
		ans+=i-1-sum;ans%=mod;
		add(e[i],1);
	}
	printf("%d\n",ans);
	return 0;
}

\(D1T3\)这道题已经反复做过很多遍了.博客

\(D2T1\)这道题我已经做得要吐了.不说了.

\(D2T2\)就在我写这篇博客的今天 校内模拟赛 考了这道题\(!!!\)这道题方法很多,\(O(n)\)\(DP\)或者贪心,\(O(nlogn)\)\(DP\)+树状数组/线段树.注意到两种序列其实是同时存在的,即最终的合法答案序列一定是一低一高/一高一低,所以一个高的可以直接从上一个矮的转移过来,同理矮的也可以直接从上一个高的转移过来.(哦哦,好像本题是数字的大小,串题了.)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=1e5+5;
int n;
int a[N],f[N][2];
int main(){
	n=read();for(int i=1;i<=n;++i)a[i]=read();
	f[1][0]=f[1][1]=1;
	for(int i=2;i<=n;++i){
		if(a[i]>a[i-1]){
			f[i][0]=f[i-1][1]+1;
			f[i][1]=f[i-1][1];
		}
		if(a[i]<a[i-1]){
			f[i][0]=f[i-1][0];
			f[i][1]=f[i-1][0]+1;
		}
		if(a[i]==a[i-1]){
			f[i][0]=f[i-1][0];
			f[i][1]=f[i-1][1];
		}
	}
    printf("%d\n",max(f[n][0],f[n][1]));
	return 0;
}

posted on 2019-11-03 21:50  PPXppx  阅读(201)  评论(0编辑  收藏  举报