由於這次題比較難,大部分題都是參考了其他人的思路和代碼做出來的。。。
題目鏈接: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;
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------
就補這些吧!