前言
由于最近状态不好,所以怂的打小号,比赛还是很顺利的,A题3min
1A,之后看B,8min
1A,然后看C,发现二分一下就可以,之后写了5分钟写完,时间差不多15min
,但是答案应该是r我输出的l,质疑了自己十分钟才找到这个bug,C题28min
1A,之后看D,写了个n*m的组合数做法,发现想不到优化,之后想dp,得到递推式之后矩阵快速幂,整体还算顺利,只是码力这些天有些下降,75min
1A.之后被E吓到了,赛后补完之后才发现没有这么难,只是这些天思维有些下降了,警醒自己应该训练啦!
biu_biubiu
r
a
t
i
n
g
+
=
163
rating+=163
rating+=163 1665->1828
A. Best Subsegment
题意
设x为一个序列中的最大值,找到给定序列中最长的x的长度。
做法
按题意模拟即可。
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 1e5+5;
int a[maxn];
int main()
{
int n,x;
scanf("%d",&n);
int ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
ans=max(ans,a[i]);
}
int maxx=0;
int cnt=0;
for(int i=1;i<=n;i++)
{
if(a[i]==ans)
{
cnt=0;
while(i<=n&&a[i]==ans)
{
cnt++;
i++;
}
maxx=max(maxx,cnt);
}
}
printf("%d\n",maxx);
return 0;
}
B. Emotes
题意
给你n个数,你要在这些数中每次选出一个数写下来,一共写出m个数,一个数不能连续写k次,求最中m个数的最大和。
2
≤
n
≤
2
∗
1
0
5
2 \leq n \leq 2*10^5
2≤n≤2∗105
1
≤
m
≤
2
∗
1
0
9
1 \leq m \leq 2*10^9
1≤m≤2∗109
做法
注意到m很大,我们选择的方案一定是k个最大值,1个次大值,k个最大值,1个次大值…
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e5+5;
ll a[maxn];
int main()
{
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
sort(a+1,a+1+n);
ll ans=0;
ans=1LL*(m/(k+1))*(1LL*a[n]*k+a[n-1]);
ans=ans+1LL*(m%(k+1))*a[n];
printf("%lld\n",ans);
return 0;
}
C. Magic Ship
题意
有一艘船,每个单位时间可以选择向U,D,L,R四个方向移动一个单位长度或者在原地不动
并且船会被风向具有周期性的风往当前风的方向吹一个单位长度,现在给你船的起始位置和目标位置
求船最少需要多少单位时间能到达目的地。
做法
答案满足二分性,因为如果我们已经到达,就可以一直与风反向行驶,保持船不动,所以具有二分性。
我们只需要二分答案,并且O(n)进行check船是否可以到达即可。
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
char str[maxn];
ll u,l;
int n;
ll x_1,x_2,y_1,y_2;
ll abs_(ll x)
{
if(x<0) return -x;
return x;
}
bool check(ll mid)
{
ll shang=mid/n*u;
ll zuo=mid/n*l;
int tmp=mid%n;
for(int i=1;i<=tmp;i++)
{
if(str[i]=='U') shang++;
if(str[i]=='D') shang--;
if(str[i]=='L') zuo++;
if(str[i]=='R') zuo--;
}
ll xx1=x_1-zuo;
ll yy1=y_1+shang;
if(abs_(xx1-x_2)+abs_(yy1-y_2)<=mid) return true;
return false;
}
int main()
{
scanf("%lld%lld%lld%lld",&x_1,&y_1,&x_2,&y_2);
scanf("%d",&n);
scanf("%s",str+1);
for(int i=1;i<=n;i++)
{
if(str[i]=='U') u++;
if(str[i]=='D') u--;
if(str[i]=='L') l++;
if(str[i]=='R') l--;
}
ll l=0,r=2000000000000000000,mid;
while(l<=r)
{
mid=(l+r)/2;
if(check(mid)) r=mid-1;
else l=mid+1;
}
if(l==2000000000000000001) printf("-1\n");
else printf("%lld\n",l);
return 0;
}
D. Magic Gems
题意
长度为n的01串0的个数是m的倍数,而且每m个0都是连续的方案数。
1
≤
n
≤
1
0
18
1 \leq n \leq 10^{18}
1≤n≤1018
2
≤
m
≤
100
2 \leq m \leq 100
2≤m≤100
做法
可以看到n很大,我们首先想n很小的时候怎么做,我们用dp[i]表示长度为i的串的合法方案数,第i位放1的方案数为dp[i-1],第i位放0的方案数位dp[i-m],所以得到转移方程dp[i]=dp[i-m]+dp[i-1],由于m很小,很显然可以通过矩阵快速幂加速这个转移。
时间复杂度
O
(
m
3
∗
l
o
g
(
n
)
)
O(m^3*log(n))
O(m3∗log(n))
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int Mod=1000000007;
void add(ll &x,ll y)
{
x+=y;
if(x>=Mod) x%=Mod;
}
struct mat
{
ll jz[105][105];
};
int m;
mat mat_mul(mat x,mat y)
{
mat res;
memset(res.jz,0,sizeof(res.jz));
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
for(int k=0;k<m;k++)
add(res.jz[i][j],x.jz[i][k]*y.jz[k][j]);
return res;
}
ll power_mod (ll P)//.res是系数矩阵,ans是变换矩阵!!左->ans,右->res.!!
{
mat ans,res;
for(int i=0;i<m;i++)
{
for(int j=0;j<m;j++)
{
ans.jz[i][j]=0;
res.jz[i][j]=0;
}
}
for(int i=0;i<=m-2;i++) res.jz[i+1][i]=1;
res.jz[0][m-1]=1;
res.jz[m-1][m-1]=1;
for(int i=0;i<=m-1;i++) ans.jz[0][i]=1;
while(P>0)
{
if(P&1) ans=mat_mul(ans,res);//所以应该答案矩阵在左ans,res);
P=P>>1;
res=mat_mul(res,res);
}
return (ans.jz[0][m-1]+Mod)%Mod;//返回指定位置元素
}
int main()
{
ll n;
scanf("%lld%d",&n,&m);
if(n<=m-1) printf("1\n");
else printf("%lld\n",power_mod(n-m+1));
return 0;
}
E. Decypher the String
题意
就是给你一个长度为n的字符串的转换规则,规则是最多可以交换任意两个位置n次,得到新的字符串,现在给你一个转换之后的字符串,让你求原来的字符串。
最多提问三次,每次提问你给出一个长度为n的字符串,交互会按照规则返回转换之后的字符串。
1
≤
n
≤
1
0
4
1 \leq n \leq 10^4
1≤n≤104
做法
这道题不考虑那个转换规则,我们只知道最后每个位置一定由之前字符串的某个位置而来,而且是一一对应的。所以我们只要知道每个字符之前在哪个位置就可以,我们第一次提问用一个
26
∗
26
∗
a
,
26
∗
26
∗
b
,
26
∗
26
∗
c
.
.
.
.
.
26
∗
26
∗
z
26*26*a,26*26*b,26*26*c.....26*26*z
26∗26∗a,26∗26∗b,26∗26∗c.....26∗26∗z 这样的字符串提问,我们就知道每个字符来自哪一个块中,之后再用
26
∗
a
,
26
∗
b
,
26
∗
c
.
.
.
.
26
∗
z
26*a,26*b,26*c....26*z
26∗a,26∗b,26∗c....26∗z这样的字符串提问,就知道这个字符来自哪一个小块,因为之前确定大块,而每个大块只包含26种小块,所以可以根据答案判断这个位置来自哪个大块的哪个小块。同理最后用
a
,
b
,
c
,
d
.
.
.
z
a,b,c,d...z
a,b,c,d...z去提问,就知道每个字符来自哪个位置了,其实仔细想一下就是一个26进制。因为
2
6
3
>
10000
26^3>10000
263>10000 ,所以每个位置都有唯一的对应关系。
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<map>
using namespace std;
const int maxn = 1e5+5;
char str[maxn],tmp[maxn];
char ans[3][maxn];
char rrs[3][maxn];
map<string,int> mp;
char ou[maxn];
int main()
{
scanf("%s",tmp);
int len=strlen(tmp);
for(int i=0;i<len;i++)
{
ans[0][i]=char(i/(26*26)+'a');
ans[1][i]=char((i%(26*26))/26+'a');
ans[2][i]=char(i%26+'a');
string res="";
res+=ans[0][i];
res+=ans[1][i];
res+=ans[2][i];
mp[res]=i;//i位置三次查询串的字符为res
}
for(int i=0;i<3;i++) ans[i][len]='\0';
cout<<"?"<<" "<<ans[0]<<endl;
scanf("%s",str);
for(int i=0;i<len;i++) rrs[0][i]=str[i];
cout<<"?"<<" "<<ans[1]<<endl;
scanf("%s",str);
for(int i=0;i<len;i++) rrs[1][i]=str[i];
cout<<"?"<<" "<<ans[2]<<endl;
scanf("%s",str);
for(int i=0;i<len;i++) rrs[2][i]=str[i];
for(int i=0;i<len;i++)
{
string res="";
res+=rrs[0][i];
res+=rrs[1][i];
res+=rrs[2][i];
ou[mp[res]]=tmp[i];
}
ou[len]='\0';
cout<<"!"<<" "<<ou<<endl;
return 0;
}