(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
",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
",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
",max(f[n][0],f[n][1]));
return 0;
}