由于这次题比较难,大部分题都是参考了其他人的思路和代码做出来的。。。
题目链接:https://www.nowcoder.com/acm/contest/140#question
A题
算是这场比赛的签到题,过的人也最多,算是一道简单dp吧。
f[i][0]表示共走i米,最后一步走1米的结果;f[i][1]表示共走i米,最后一步跑k米的结果。
转移方程:f[i][0]=(f[i-1][0]+f[i-1][1])%MOD
f[i][1]=f[i-k][0] (i>=k)
代码:
#include<cstdio>
using namespace std;
#define MAXN 100009
const int MOD = 1e9+7;
int f[MAXN][2];
int main(){
int Q, k;
scanf("%d%d", &Q, &k);
f[0][0]=1;
f[1][0]=1;
for(int i=1; i<MAXN; ++i){
f[i][0]=(f[i-1][0]+f[i-1][1])%MOD;
if(i-k>=0) f[i][1]=f[i-k][0];
}
for(int i=1; i<MAXN; ++i){
f[i][0]+=f[i][1];
f[i][0]%=MOD;
f[i][0]+=f[i-1][0];
f[i][0]%=MOD;
}
int L, R;
while(Q--){
scanf("%d%d", &L, &R);
printf("%d\n", ((f[R][0]-f[L-1][0])%MOD+MOD)%MOD);
}
return 0;
}
D题
这道题过的人也比较多
把每个位置的值看成一个个点,如图。
在极小值处买
入,极大值处卖出即可。遇到值连续相同的点,删掉即可。注意一下开始和结尾的特殊处理。
代码:
#include<cstdio>
#include<iostream>
#define maxn 100005
using namespace std;
int a[maxn];
int main(){
int T;
scanf("%d",&T);
while(T--){
int n;
int Max=0;
scanf("%d",&n);
a[0]=-1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]==a[i-1]){
i--;n--;
}
Max=max(a[i],Max);
}
a[0]=Max;
a[n+1]=0;
int down;
down=-1;
int time=0;
long long ans=0;
for(int i=1;i<=n;i++){
if(a[i]<a[i-1]&&a[i]<a[i+1])down=a[i];
if(a[i]>a[i-1]&&a[i]>a[i+1])
// if(down!=-1)
{
ans+=(a[i]-down);
time++;
}
}
printf("%lld %d\n",ans,time*2);
}
return 0;
}
前面的题都是比赛时候做出来的,后面几道题就是窝补得了
I题
比赛的时候刚了很长时间,不过好像结论的方向猜错了。
其实示意图是这样的
这个是偶数时的情况:
这个是奇数时的情况,虚线表示四个里选一个。
结论是没陷阱时为2n-n%2
加上陷阱后除掉不符合条件的即可
#include<cstdio>
#include<algorithm>
using namespace std;
int c[100010];
int r[100010];
int main()
{
int n,m;
int x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
r[x]=1;
c[y]=1;
}
int ans;
if(n%2==1)
{
ans=2*n-1;
if(r[(n+1)/2]&&c[(n+1)/2])
ans--;
}
else
ans=2*n;
for(int i=1;i<=n;i++)
{
if(i==(n+1)/2)
continue;
else
{
if(c[i])
ans--;
if(r[i])
ans--;
}
}
ans=max(ans,0);
printf("%d\n",ans);
return 0;
}
J题
二维前缀和+位运算。
参考:https://www.cnblogs.com/OIerShawnZhou/p/7348088.html
里面对二维前缀和及其运算讲的很清楚,代码里也基本上用的这个思想。
做法就是官方给的做法
代码(参考某位神犇):
#include<cstdio>
#include<cstring>
using namespace std;
int map[2000005], s[2000005],num[2000005][2],x1[2000005],y1[2000005],x2[2000005],y2[2000005],kk[2000005];
int main() {
int n, m, t,res=0;
scanf("%d%d%d", &n, &m, &t);
int i, j;
for (i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
scanf("%d",&map[i*m+j]);
}
for (i=1;i<=t;i++)
scanf("%d%d%d%d%d",&x1[i],&y1[i],&x2[i],&y2[i],&kk[i]);
for (int k = 1; k <= 21; k++)
{
for (i = 1; i <= n; i++)
for (j = 1; j <= m; j++)
num[i*m + j][0] = num[i*m + j][1] = 0;
for(int l=1;l<=t;l++)
{
int e=(kk[l]>>(k-1))&1;
num[x1[l]*m+y1[l]][e]++;
num[(x2[l]+1)*m+y2[l]+1][e]++;
num[x1[l]*m+y2[l]+1][e]--;
num[(x2[l]+1)*m+y1[l]][e]--;
}
for (i = 1; i <= n; i++) {
for (j = 1; j <= m; j++) {
int c = 0, d = 0;
if (((map[i*m+j]>>(k-1))&1) == 0)
c++;
else
d++;
num[i*m + j][0] = num[i*m + j][0] + num[(i - 1)*m + j][0] + num[i*m + j - 1][0] - num[(i - 1)*m + j - 1][0];
num[i*m + j][1] = num[i*m + j][1] + num[(i - 1)*m + j][1] + num[i*m + j - 1][1] - num[(i - 1)*m + j - 1][1];
c += num[i*m + j][0];
d += num[i*m + j][1];
if (c > 0 && d > 0)
s[i*m + j]++;
}
}
}
for (i = 1; i <= n; i++) {
for (j = 1; j <= m; j++) {
if (s[i*m + j])
res++;
}
}
printf("%d\n", res);
return 0;
}
G题
总体思路:二分+尺取。细节较多。以下内容转载地址:https://blog.csdn.net/LSD20164388/article/details/81152298
题意:有n个位置,每个位置的坐标为x[i],有桶a[i]个。你现在要把若干个桶移动到同一个位置,求在移动总距离不超过T/2的情况下,最多可以将多少个木桶移动到同一个位置?(n<=5e5,T<=1e18,x[i]<=1e9,a[i]<=1e5)
思路:二分。如官方题解所说,我们要使移动的总距离最小,那么最终被移动的桶在数轴上一定是一段连续的区间。如果固定了这个区间,那么最优方案就是把这个区间的所有桶移动到这个区间的某个位置。而这个位置在这个区间内满足先单减再单增,在总体区间满足单调递增。我们固定这段连续区间的起点,那么区间的终点也是总体单增的。因此只需要O(n)枚举区间左端点。
我们用s[i]表示前i个位置有多少个桶,t[i]表示前i个位置的所有桶从0坐标移动过来的距离和。
假设我们二分到可以搬x个桶到同一个位置,当前判断的连续区间为[lp+1,rp],则首先s[rp]-s[lp]>=x。此时会有两种情况,就是我们在这个区间内要挑出x个桶。显然多余的桶一定在a[rp]或者在a[lp](因为我们单调枚举左端点)。当在a[rp]时,我们就不需要搬那多余的(s[rp]-s[lp]-x)个桶了。于是我们枚举这x个桶搬到某个位置x[i]时,区间下标[lp+1,i]的桶搬到x[i]的总距离为:
(s[i]-s[lp])*d[i]-(t[i]-t[lp]);
区间下标[i,rp]的桶搬到x[i]的总距离为(除去多余的桶和上一步已经搬了的桶):
t[rp]-t[lp]-(s[rp]-s[lp]-x)*d[rp]-(t[i]-t[lp])-(x-(s[i]-s[lp]))*d[i];
以上式子需要好好思考一下。
当在a[lp]时与以上情况类似。此时需要从右往左枚举区间右端点。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5e5+10;
int n;
ll T,o,p,lp,rp,a[maxn],d[maxn],t[maxn],s[maxn],mp;
ll c1(int i)
{
return (s[i]-s[lp])*d[i]-(t[i]-t[lp])+(o-t[i]+t[lp])-(p-s[i]+s[lp])*d[i];
}
ll c2(int i)
{
return -(s[rp]-s[i])*d[i]+(t[rp]-t[i])-(o-t[rp]+t[i])+(p-s[rp]+s[i])*d[i];
}
bool jud(ll x)
{
p=x;
lp=0;rp=1;mp=1;
while(1)
{
while(rp<n&&s[rp]-s[lp]<x) rp++;
if(s[rp]-s[lp]<x) break;
o=t[rp]-t[lp]-(s[rp]-s[lp]-x)*d[rp];
while(mp<n&&c1(mp)>c1(mp+1))mp++;
if(c1(mp)<=T)return 1;
lp++;
}
lp=n-1;rp=n;mp=n;
while(1)
{
while(lp>0&&s[rp]-s[lp]<x) lp--;
if(s[rp]-s[lp]<x) break;
o=t[rp]-t[lp]-(s[rp]-s[lp]-x)*d[lp+1];
while(mp>1&&c2(mp)>c2(mp+1))mp--;
if(c2(mp)<=T)return 1;
rp--;
}
return 0;
}
int main()
{
scanf("%d %lld",&n,&T);
T>>=1;
for(int i=1;i<=n;i++)
scanf("%lld",&d[i]);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)
{
s[i]=s[i-1]+a[i];t[i]=t[i-1]+a[i]*d[i];
}
ll l=0,r=s[n]+1;
ll ans=0;
while(l<=r)
{
ll mid=(l+r)>>1;
if(jud(mid))
{
l=mid+1;
ans=max(ans,mid);
}
else r=mid-1;
}
printf("%lld\n",ans);
return 0;
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------
就补这些吧!