題目鏈接
賽中我就做出A,B兩題,結(jié)果后來一直卡E。實際上這套題C、E并不是代碼不好寫,C就是bfs的板子,E就是推個式子,但是因為細節(jié)很多,很容易注意不到。
D、F算是壓軸題,難度大概能對標cf div2的D甚至是E吧,遠遠不是出題人所說的對標cf div2的A~C難度。
D是一個模擬,根本想不到要用隊列,就算想到了,也不會想到金幣有可能爆long long而不能在這時接著加(和游戲里面差不多)。而且模擬的過程細節(jié)結(jié)果過多,只能說出題人想防小白AK那真是沒辦法~
F是雙指針+差分,這個題主要是讀完題之后很難聯(lián)想到要用差分這個算法,因為你難以想到這m個約束對應(yīng)于m個條件。
A 超市里掃貨直接按題意模擬。
#includeusing namespace std;
const int maxn = 1e5 + 5;
int v[maxn];
int main()
{int n,V;
cin>>n>>V;
for(int i=1;i<=n;i++)cin>>v[i];
long long ans=1,sum=0;
for(int i=1;i<=n;i++){if(sum+v[i]<=V)sum+=v[i];
else sum=v[i],ans++;
}
cout<
B 柜臺結(jié)賬仍然是模擬,只是細節(jié)有點多。
#includeusing namespace std;
const int maxn = 1e5 + 5;
bool pd(string s){bool flag=true;
for(int i=0;iif(s[i]-'0'){flag=false;
break;
}
}
return flag;
}
int pd5(string s){int ans;
if(s[0]-'0'==5){bool flag=true;
for(int i=1;iif(s[i]-'0'){flag=false;ans=1;break;
}
}
if(flag==true){ans=3;
}
}
else if(s[0]-'0'>5){ans=1;
}
else{ans=2;
}
return ans;
}
int main()
{string a1,a2;
cin>>a1>>a2;
if(pd5(a2)==1){cout<<"Happy birthday to MFGG"<if(!pd(a2))cout<<"Happy birthday to YXGG"<if((a1[a1.length()-1]-'0')&1){cout<<"Happy birthday to MFGG"<if(!pd(a2))cout<<"Happy birthday to YXGG"<
C 小喵覓食這個題用不了dfs,因為dfs不能直接求出每個點的最短距離。但是bfs可以,因為可以在bfs的每一層利用dp的思想將層數(shù)狀態(tài)轉(zhuǎn)移。
先bfs求每個點以小貓為起點的單源最短路,再bfs求每個點以PLMM為起點的單源最短路,然后凡是滿足條件的點都要算一遍最短路之和。注意,千萬不能意淫,因為你不是計算機!每個滿足約束的點都要算一遍其最短距離。
#includeusing namespace std;
int dir[4][2]={{0,1},{1,0},{-1,0},{0,-1}};
int n,m,r1,r2;
const int maxn=1e3+5;
char s[maxn][maxn];
struct node{int x,y;
};
queueq;
int cx,cy,px,py;
bool vis[maxn][maxn];
int disc[maxn][maxn],disp[maxn][maxn],dis[maxn][maxn];
void bfs(int sx, int sy){node now;
now.x=sx;
now.y=sy;
q.push(now);
dis[sx][sy]=0;
vis[sx][sy]=1;
while(!q.empty()){node next;
now = q.front();
q.pop();
for(int i=0;i<4;i++){next.x=now.x+dir[i][0];
next.y=now.y+dir[i][1];
if(next.x>=1&&next.y>=1&&next.x<=n&&next.y<=m){if(s[next.x][next.y]!='*'&&!vis[next.x][next.y]){vis[next.x][next.y]=1;
dis[next.x][next.y]=dis[now.x][now.y]+1;
q.push(next);
}
}
}
}
}
int main(){cin>>n>>m;
cin>>r1>>r2;
for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){scanf(" %c",&s[i][j]);
if(s[i][j]=='P')px=i,py=j;
if(s[i][j]=='M')cx=i,cy=j;
}
}
memset(vis,0,sizeof(vis));
memset(dis,0,sizeof(dis));
bfs(cx,cy);
for(int i=1;i<=n;i++){for(int j=1;j<=m;j++)disc[i][j]=dis[i][j];
}
for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(disc[i][j]==0)disc[i][j]=1e9;
}
}
disc[cx][cy]=0;
memset(vis,0,sizeof(vis));
memset(dis,0,sizeof(dis));
bfs(px,py);
for(int i=1;i<=n;i++){for(int j=1;j<=m;j++)disp[i][j]=dis[i][j];
}
for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(disp[i][j]==0)disp[i][j]=1e9;
}
}
disp[px][py]=0;
int ans=1e9;
for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(abs(cx-i)+abs(cy-j)<=r2){if(disp[i][j]<=r1){ans=min(ans,disp[i][j]+disc[i][j]);
}
}
}
}
if(ans==1e9)ans=-1;
cout<
D 石油大亨這個代碼以我現(xiàn)在一個cf綠名的碼力寫不出來,估摸著起碼得藍名才能寫出這。這個代碼是出題人的,但是注釋是我自己加的,讓我寫我寫不出來,等我上了藍名再回來自己寫一遍。
#includeusing namespace std;
typedef long long ll;
const int M = 1e5+5;
const ll linf = 0x3f3f3f3f3f3f3f3f;
//即4557430888798830600,4.557×10^18
//long long大9223372036854775807,9.223×10^18
int n, ec, et, p; ll s;
int t[M];
void work()
{scanf("%d %d %d %d %lld", &n, &ec, &et, &p, &s);
for (int i = 1; i<= n; ++i) scanf("%d", &t[i]);
if (s< ec) {printf("-1\n");return;}
//如果初始金額小于造一名工程師的金額,就不可能實現(xiàn)
//那么下面的就是一開始的錢至少夠造一個工程師
queueq; //存儲工程師占領(lǐng)油田的時間
ll cur_time = 0;//當(dāng)前時間為0
for (int i = 1, oil_cnt = 0; i<= n; ++i) {//第i個油田,占領(lǐng)油田數(shù)一開始為0
ll next_time;//下一次可以開始訓(xùn)練工程師的最小時間點
if(i==1)next_time=0;//如果從第1個油田算起,那就是現(xiàn)在就可以開始訓(xùn)練
else next_time=cur_time+et;//如果不是第一個油田,那就是當(dāng)前時間點+et秒訓(xùn)練時間
//當(dāng)前工程師占領(lǐng)油田的時間點,在下一次可以開始訓(xùn)練工程師的最小時間點及以前
while (!q.empty() && q.front()<= next_time) {//計算從當(dāng)前到next_time的金額收益
//如果金額沒爆long long,才接著收錢,否則就不收了,不然爆了沒法存
//雖然linf不是long long大值,但大于這個值再存金幣也沒有意義
//之所以不設(shè)置long long大值,還是為了大得適當(dāng),防止意外溢出
if(s< linf) s += oil_cnt * (q.front() - cur_time) * p;
//當(dāng)前的油田數(shù)乘p金額是一秒的;
//經(jīng)歷的時間是下一名工程師占領(lǐng)油田的時間點-當(dāng)前工程師占領(lǐng)油田的時間點
//因為在這一段時間油田數(shù)是固定的
cur_time = q.front();//更新當(dāng)前工程師占領(lǐng)油田的時間點
q.pop();//因為已經(jīng)用了,所以彈出隊列
oil_cnt++;//占領(lǐng)的油田數(shù)加1,因為更新一次當(dāng)前時間,就被占領(lǐng)一個油田
}
if (cur_time< next_time) { //如果當(dāng)前工程師占領(lǐng)油田的時間點,小于可以訓(xùn)練下一名工程師的最小時間點
if(s< linf) s += oil_cnt * (next_time - cur_time) * p;
//當(dāng)前的油田數(shù)乘p金額是一秒的;
//經(jīng)歷的時間是可以訓(xùn)練下一名工程師的最小時間點-當(dāng)前工程師占領(lǐng)油田的時間點
cur_time = next_time;//更新當(dāng)前時間點為可以訓(xùn)練下一名工程師的最小時間點
}
if (s< ec) { //如果當(dāng)前金額不足以訓(xùn)練一名工程師
while (!q.empty() && s + oil_cnt * (q.front() - cur_time) * p< ec) { //如果當(dāng)前工程師占領(lǐng)油田時,金額不足以訓(xùn)練下一名工程師
s += oil_cnt * (q.front() - cur_time) * p;//先把這段時間的錢加上
cur_time = q.front();//先更新當(dāng)前時間
q.pop();//當(dāng)前工程師占領(lǐng)油田的時間點已經(jīng)用過了,因此彈出隊列
oil_cnt++;//占領(lǐng)的油田數(shù)加1
}
//金額不足,需要等待
ll wait_time = (ec - s + (ll)oil_cnt * p - 1) / (oil_cnt * p);
//需要等待wait_time的時間
//還需要金額ec-s,
s += oil_cnt * wait_time * p;//加上等待時間內(nèi)生產(chǎn)的錢
cur_time += wait_time;//更新當(dāng)前時間
}
s -= ec;//更新當(dāng)前金額
q.push(cur_time + et + t[i]);//工程師占領(lǐng)油田的時間點等于
//當(dāng)前時間點+et秒訓(xùn)練時間+工程師去油田的時間
//注意,每次循環(huán)結(jié)束后才加入隊列
//因此最后一次循環(huán)的cur_time+et+t[n]就是答案
}
printf("%lld\n", cur_time + et + t[n]);
}
int main()
{work();
return 0;
}
E 排隊就是分三類,正序數(shù),負序數(shù),零序數(shù)。有對稱性知正序數(shù)和負序數(shù)數(shù)量相同,那么求出全排列,再減去零序數(shù)的數(shù)量,最后除2就是逆序數(shù)的數(shù)量。
零序數(shù)的數(shù)量用排列組合求。
#includeusing namespace std;
typedef unsigned long long ll;
const int N=1e5+10;
const ll mod=1e9+7;
ll cnt[N],a[N],j[N];
ll js(ll x){if(x!=0){return x*(x-1)/2;
}
return 0;
}
int main()
{ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
ll n;cin>>n;
for(int i=0;icin>>a[i];
cnt[a[i]]++;
}
ll sum=n*(n-1)/2;
for(int i=0;isum-=js(cnt[i]);
}
sum%=mod;
for(int i=3;i<=n;i++)
{sum*=i;
sum%=mod;
}
if(n==1)sum/=2;
cout<
F 選座椅由于區(qū)間連續(xù),所以用雙指針控制區(qū)間長度,不斷尺取。如果長度區(qū)間[L,R]滿足m個條件,則長度區(qū)間[L,R+1]也滿足,以此類推。那么就可以看出可以用差分來對一段連續(xù)區(qū)間進行加操作。
#includeusing namespace std;
#define int long long
const int maxn=1e5+5;
const int mod=1e9+7;
int n,m;
vectorv[maxn];
int cnt[maxn],deta[maxn],fac[maxn];
void add(int id, int &now){for(auto u:v[id]){cnt[u]++;
if(cnt[u]==1)now++;
}
}
void del(int id, int &now){for(auto u:v[id]){cnt[u]--;
if(cnt[u]==0)now--;
}
}
signed main(){cin>>n>>m;
for(int i=1;i<=3;i++){for(int j=1;j<=m;j++){int u;
cin>>u;
v[u].push_back(j);
}
}
int now=0;
memset(deta,0,sizeof(deta));
for(int i=1,j=1;i<=n;i++){while(j<=n&&nowint L=(j-1)-i+1;
int R=n-i+1;
deta[L]++;
deta[R+1]--;
}
del(i,now);
if(i==j)j++;
}
fac[0]=fac[1]=1;
for(int i=2;i<=n;i++){fac[i]=fac[i-1]*i%mod;
}
for(int i=1;i<=n;i++){deta[i]=deta[i]+deta[i-1]%mod;
cout<
小結(jié)這套題難度比??托“自沦?難了不少,很多結(jié)論如果不熟練那就直接無了。但是多學(xué)多練,總有一天我終將成為你。
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧