A:Save the Princess
題目:點擊打開鏈接
題意:n個人橫向排好隊,其中左數第k個是公主,LYF和BH分別可以殺掉隊列最左邊的或者最右邊的人,直到某個人拯救公主,兩個人都會選擇最佳殺人方案,LYF先殺,問誰能拯救公主。
分析:
當公主在隊列兩端時,LYF先行動,所以LYF是贏家,其他情況時,當某個人正好殺掉與公主相鄰的人時,另一個人肯定是贏家,所以不能先殺與公主相鄰的人,因爲兩人交互行動,且LYF先行動,在殺與公主相鄰的人之前可以任選最左或者最右的人殺,因此只要確定n-3的奇偶性,就可以判斷誰是贏家了。
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <map>
#define PR pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define sqr(x) ((x)*(x))
#define ll unsigned long long
const ll INF = 1e18;
const int inf=0x3f3f3f3f;
const int M=18;
const int N=100;
const int MOD=1000000007;
const double eps=1e-10;
const double pi=acos(-1.0);
using namespace std;
int main()
{
int T,n,k;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&k);
if(k==1||k==n) puts("LYF");
else
{
if(n&1) puts("BH");
else puts("LYF");
}
}
}
B:巴比倫花園
題目:點擊打開鏈接
題意:有n個階梯高度分別爲h_i(1<=i<=n),有q個詢問,每次詢問區間[l,r],可以將h_i(l<=i<=r)改成f_i,滿足f_i<=h_i,f_i>=1,f_(i+1)-f_i=k,求每次詢問滿足以上條件的最長長度的連續數列長度。
分析:
因爲要求出區間內滿足條件的最長數列,所以可以先處理出從i出發,最長可以連續增長到哪個點。我們發現一旦i可以到達j(j>i),i+1就也一定能達到j,因此到達的最長位置是不遞減的,因爲最左端高度從1開始,得到的是最長的長度。我們可以先dp一下,dp[i]表示i能到的最遠位置,len表示當前的階梯的高度,cur表示每次達到的最遠位置。
處理完dp後,對於區間[l,r],可以先考慮左端點在[l,r]內,最遠距離超過r的點,因爲這可能有很多點,要取最長距離,就是求最左端的點的座標pos,這個可以通過二分dp值得到,如果pos<=l的話,那說明有一條最長的階梯能穿過這個區間,那麼最長的長度就是這個區間的長度。否則的話區間的最長長度還可能是從[l,pos-1]中的某一個點出發,走過的最遠距離的長度,這些點的右端點是不會超過r的,因此我們需要將區間內的最長長度(dp[i]-i+1)可以用線段樹或者st表保存起來,在這裏我用了線段樹,因爲還可以保存區間內的點可以達到的最遠距離,之前就不用二分dp值,只需要線段樹查詢一下區間內最遠距離超過r的最近的點就行了,保存其長度,然後查詢左端點在[l,pos-1]內最長的長度,比較兩者的大小即可
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <map>
#define PR pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define sqr(x) ((x)*(x))
#define ll long long
const ll INF = 1e18;
const int inf=0x3f3f3f3f;
const int M=18;
const int N=100100;
const int MOD=1000000007;
const double eps=1e-9;
const double pi=acos(-1.0);
using namespace std;
int dp[N];
ll h[N],k;
int n,q,l,r;
int mxlen[N<<2],mxp[N<<2];
void pushup(int rt)
{
mxlen[rt]=max(mxlen[rt<<1],mxlen[rt<<1|1]);
mxp[rt]=max(mxp[rt<<1],mxp[rt<<1|1]);
}
void build(int l,int r,int rt)
{
if(l==r)
{
mxp[rt]=dp[l];
mxlen[rt]=dp[l]-l+1;
return;
}
int m=l+r>>1;
build(lson);
build(rson);
pushup(rt);
}
int query_p(int p,int l,int r,int rt)
{
if(l==r) return l;
int m=l+r>>1;
if(mxp[rt<<1]>=p) return query_p(p,lson);
else return query_p(p,rson);
}
int query_s(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R) return mxlen[rt];
int m=l+r>>1;
int ans=0;
if(L<=m) ans=max(ans,query_s(L,R,lson));
if(m<R) ans=max(ans,query_s(L,R,rson));
return ans;
}
int main()
{
int T,i,j;
scanf("%d",&T);
while(T--)
{
scanf("%d%lld%d",&n,&k,&q);
for(i=1;i<=n;i++)scanf("%lld",&h[i]);
int cur=1;
ll len=1;
for(i=1;i<=n;i++)
{
dp[i]=cur;
if(cur==n) dp[i]=n;
else
{
while(h[cur+1]>=len+k)
{
len+=k;
dp[i]=++cur;
}
len-=k;
}
}
build(1,n,1);
while(q--)
{
scanf("%d%d",&l,&r);
int pos=query_p(r,1,n,1);
if(pos<=l) {printf("%d\n",r-l+1);continue;}
int ans=r-pos+1;
if(pos-1>=l) ans=max(ans,query_s(l,pos-1,1,n,1));
printf("%d\n",ans);
}
}
}
C:極差
題目:點擊打開鏈接
題意:有n個數,求這些數的最大值與最小值之間的差距
分析:
先定義最小值爲正無窮,最大值爲零,每次輸入一個數都判斷最小值是不是大於它,如果最小值大於它,則把該值賦值給最小值,再判斷最大值是不是小於它,如果最大值小於它,就把該值賦值給最大值,最後將最大值與最小值相減
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <map>
#define PR pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define sqr(x) ((x)*(x))
#define ll long long
const ll INF = (1ULL<<63)-1;
const int inf=0x3f3f3f3f;
const int M=100010;
const int N=100100;
const ll MOD=1000000007;
const double eps=1e-8;
const double pi=acos(-1.0);
using namespace std;
int a[N],n;
int main()
{
int T,i;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
int mx=0,mn=inf;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
mx=max(mx,a[i]);
mn=min(mn,a[i]);
}
printf("%d\n",mx-mn);
}
}
題目:點擊打開鏈接
題意:KI爲了參加聖盃戰爭,決定向魔術師CS請教,CS爲了檢測KI身爲御主的潛力,決定進行一場模擬聖盃戰爭:
CS召喚了n個實驗怪獸,第i號怪獸在i這個位置出。並把KI召喚出的第i位從者安排在pos(i)處,總共有m位從者。
第i只怪獸有戰鬥力atk(i), 而i號從者的體力爲AP(i)。如果從者想要移動,他必須戰勝他當前位置處的怪獸,戰勝的條件爲AP>=atk, 然後該從者的AP會減少atk, 注意從者只能從i移動到i+1,或移動到i-1處,注意一旦開始移動就只能向一個方向移動了。
作爲優秀的御主,必須要有傑出的判斷力,所以CS會給出Q次詢問,每次給出一個區間[l, r]: 要求KI只能派出一個從者,並能打敗這個區間中儘可能多的怪物,求能打敗的最大怪物數。強制在線。
分析:
一個從者可以往左或者往右走,直至走到體力爲零,因此最多形成兩條線段,因爲求的是給定區間內的最多殺敵數,且只能使用一個從者,因此可以把題意轉化爲,求詢問的一條線段與給定的多條線段中交集最大的值。
我們可以先對至少可以殺掉一個敵人的從者,向左和向右能殺敵的區間。我們可以先預處理出atk的前綴和以及後綴和,進行兩次二分,確定從者最遠達到的兩個座標,並根據兩條距離的端點座標,求出在區間[1,n]上點i最遠達到的座標mxR_i,對此我們再用pair記錄所有線段的右端點和對應的左端點,排序。
接下來用主席樹,把區間[1,n]每一個點建立一棵線段樹,維護當前的點中形成的最長的線段長度mxlen和最右端點mxr,其中root_0這棵樹的mxlen和mxr都爲-inf,當點i不是某個線段的端點時,root_i這顆線段樹直接複製前一棵,即root[i]=root[i-1],當點i是某個線段的左端點l時,因爲此時線段不完整,所以取mxlen=-inf,mxr=r,r是該線段的右端點。當i是某個線段的右端點時,因爲之前的線段已經被排序,右端點相同時第一個一定是左端點最小的,所以我們直接把該線段的長度和右端點記錄到root_i這棵線段樹中。update每一個點時,比較兩子節點的mxlen和mxr值,取大值爲當前節點的mxlen和mxr值。這個和一般線段樹的pushup相同。
記錄完[1,n]上的線段後,開始詢問與[pl,pr]的最大交集。有以下四個步驟:
①求出左端點在[1,pl-1]內的線段的最大右端點值res,ans=max(ans,res-pl+1)
②二分求出右端點在[pr+1,n]內的線段的最小左端點值res,ans=max(ans,r-res+1)
③求出線段在[pl,pr]內的最長長度res,ans=max(ans,res)
④最後注意ans不會超過pr-pl+1且不會小於0,ans=max(min(ans,pr-pl+1),0)。
在第二步時,二分步驟爲,判斷當前區間是否被詢問區間包含,若包含,則判斷其mxr是否大於pr,若小於等於pr,返回無效值inf,若此時已經是葉子節點了,則返回座標l,若不包含,則先向左節點判斷,如果得到座標l,則直接返回,否則向右判斷。
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <map>
#define PR pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define sqr(x) ((x)*(x))
#define ll long long
const ll INF=1e18;
const int inf=0x3f3f3f3f;
const int M=18;
const int N=101000;
const int MOD=1000000007;
const double eps=1e-9;
const double pi=acos(-1.0);
using namespace std;
struct node{int l,r,mxlen,mxr;}T[N*30];
int n,m,q,atk[N*2],pos[N/2],ap[N/2],mxR[N*2],l,r,ans,cnt;
int Tcnt,root[N*2];
ll pre[N*2],repre[N*2];
PR a[N];
void update(int l,int r,int &x,int y,int L,int R,int len)
{
x=++Tcnt;T[x]=T[y];
if(l==r)
{
T[x].mxlen=len;
T[x].mxr=R;
return;
}
int m=l+r>>1;
if(L<=m) update(l,m,T[x].l,T[y].l,L,R,len);
else update(m+1,r,T[x].r,T[y].r,L,R,len);
T[x].mxlen=max(T[T[x].l].mxlen,T[T[x].r].mxlen);
T[x].mxr=max(T[T[x].l].mxr,T[T[x].r].mxr);
}
int query_l(int l,int r,int x,int L,int R)
{
if(L>R) return -1;
if(L<=l&&r<=R) return T[x].mxr;
int m=l+r>>1,res=-inf;
if(L<=m) res=max(res,query_l(l,m,T[x].l,L,R));
if(m<R) res=max(res,query_l(m+1,r,T[x].r,L,R));
return res;
}
int query_r(int l,int r,int x,int L,int R)
{
if(L<=l&&r<=R)
{
if(T[x].mxr<=R) return inf;
if(l==r) return l;
}
int m=l+r>>1,res=inf;
if(L<=m) res=query_r(l,m,T[x].l,L,R);
if(res!=inf) return res;
if(m<R) res=query_r(m+1,r,T[x].r,L,R);
return res;
}
int query_mid(int l,int r,int x,int L,int R)
{
if(L<=l&&r<=R) return T[x].mxlen;
int m=l+r>>1,res=-inf;
if(L<=m) res=max(res,query_mid(l,m,T[x].l,L,R));
if(m<R) res=max(res,query_mid(m+1,r,T[x].r,L,R));
return res;
}
int main()
{
int casT,i,j;
scanf("%d",&casT);
while(casT--)
{
scanf("%d%d%d",&n,&m,&q);
T[0].mxlen=-inf,T[0].mxr=-inf;
memset(mxR,0,sizeof(mxR));
memset(repre,0,sizeof(repre));
cnt=0;Tcnt=0;
for(i=1;i<=n;i++)scanf("%d",&atk[i]),pre[i]=pre[i-1]+atk[i];
for(i=n;i>=1;i--)repre[i]=repre[i+1]+atk[i];
for(i=1;i<=m;i++)scanf("%d",&pos[i]);
for(i=1;i<=m;i++)scanf("%d",&ap[i]);
for(i=1;i<=m;i++)
{
if(ap[i]<atk[pos[i]]) continue;
int L=1,R=pos[i],res1=pos[i],res2=pos[i],mid;
while(L<=R)
{
mid=L+R>>1;
if(pre[pos[i]]-pre[mid-1]<=ap[i]) res1=mid,R=mid-1;
else L=mid+1;
}
mxR[res1]=max(mxR[res1],pos[i]);
L=pos[i],R=n;
while(L<=R)
{
mid=L+R>>1;
if(repre[pos[i]]-repre[mid+1]<=ap[i]) res2=mid,L=mid+1;
else R=mid-1;
}
mxR[pos[i]]=max(mxR[pos[i]],res2);
}
for(i=1;i<=n;i++)
if(mxR[i]) a[++cnt]=MP(mxR[i],i);
sort(a+1,a+1+cnt);
int cur=0;
for(i=1;i<=n;i++)
{
root[i]=root[i-1];
if(mxR[i]) update(1,n,root[i],root[i-1],i,mxR[i],-inf);
if(cur+1<=cnt&&a[cur+1].fi==i)
{
cur++;
update(1,n,root[i],root[i],a[cur].se,a[cur].fi,a[cur].fi-a[cur].se+1);
while(cur+1<=cnt&&a[cur+1].fi==i) cur++;
}
}
ans=0;
while(q--)
{
scanf("%d%d",&l,&r);
l^=ans,r^=ans;if(l>r) swap(l,r);
int mx=0;
mx=max(mx,query_l(1,n,root[r],1,l-1)-l+1);
mx=max(mx,r-query_r(1,n,root[r],l,r)+1);
mx=max(mx,query_mid(1,n,root[r],l,r));
ans=max(min(mx,r-l+1),0);
printf("%d\n",ans);
}
}
}
題目:點擊打開鏈接
題意:輸入一個日期,判斷它離2017-10-28還差幾天,保證日期在1900-1-1和2017-10-28之間
分析:可以先寫一個函數,算出從1900-1-1開始算起,到指定日期一共有多少天,年份從1900開始枚舉到改年的前一年,一共有多少年,先默認每年都是365天,在枚舉年份的時候判斷它是不是閏年,然後再總天數裏面加上閏年的天數,把平年各月的天數存到一個數組中,下標表示月份,然後從一月開始枚舉到該月的前一個月,將天數加到總天數中,最後再加上幾號就行了,還要判斷該年是不是閏年,如果是閏年且年份大於2月,總天數還要加一。然後就可以將2017-10-28帶入函數中算出總天數再減去輸入日期帶入函數的總天數,即爲答案。
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <map>
#define PR pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define sqr(x) ((x)*(x))
#define ll unsigned long long
const ll INF = 1e18;
const int inf=0x3f3f3f3f;
const int M=18;
const int N=100;
const int MOD=1000000007;
const double eps=1e-10;
const double pi=acos(-1.0);
using namespace std;
int y[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
bool judge(int x)
{
if(x%400==0||(x%4==0&&x%100!=0)) return true;
return false;
}
int calc(int a,int b,int c)
{
int ans=0,tmp1=0,tmp2=0,i;
for(i=1899;i<=a-1;i++)
if(judge(i)) tmp1++;
for(i=1;i<=b-1;i++)
tmp2+=y[i];
ans=365*(a-1)+tmp1+tmp2+c;
if(judge(a)&&b>2) ans++;
return ans;
}
int main()
{
int T,a,b,c;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&a,&b,&c);
printf("%d\n",calc(2017,10,28)-calc(a,b,c));
}
}
題目:點擊打開鏈接
題意:農夫有一個長滿草的(x0, y0)爲圓心,r爲半徑的圓形牛欄,他要將一頭牛栓在座標(x1, y1)欄樁上,但只讓牛喫到一半草,問栓牛鼻的繩子應爲多長?
分析:
繩筆直繞一圈可以形成一個圓,這個圓就是牛可以喫的範圍,令該圓爲圓一,牛欄的圓爲圓二,圓一與圓二的覆蓋面積是圓一的一半,設圓一半徑爲x,兩圓心距離爲d。
如果圓一完全被圓二包含,那麼圓一的面積就是圓二面積的一半,答案就是r的根號二分之一。
如果圓一不被圓二覆蓋,即圓一和圓二存在兩個公共點,那麼我們需要計算出覆蓋面積,覆蓋面積可以分爲類似兩部分計算,相交的兩點與一個圓心分別形成一個扇形和一個三角形,那麼覆蓋面積的一部分就是扇形面積減去三角形面積了,另一部分同樣,兩部分面積相加就是覆蓋面積了,至於計算過程用到一些三角函數和餘弦定理,詳細看代碼。接下來可以用二分的思想,因爲d,x,r一定形成一個三角形,令左端點是l=d-r,右端點是r=d+r,每次判斷x=(l+r)/2,判斷圓二半徑爲x時,形成的覆蓋面積是否大於圓一半徑的一半,如果大於,l=m,如果小於,r=m。因爲只要保留四位小數,所以循環一百次就絕對夠了,最後得到的l值就是答案。
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <map>
#define PR pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define sqr(x) ((x)*(x))
#define ll unsigned long long
const ll INF = 1e18;
const int inf=0x3f3f3f3f;
const int M=18;
const int N=100100;
const int MOD=1000000007;
const double eps=1e-9;
const double pi=acos(-1.0);
using namespace std;
double r,s,d,tmp;
double dis(double x0,double y0,double x1,double y1) {return sqrt(sqr(x0-x1)+sqr(y0-y1));}
double calc(double x)
{
double angle1=acos((d*d+x*x-r*r)/(d*x*2))*2;
double tri1=x*x*sin(angle1)/2;
double sec1=angle1*x*x/2;
double sq1=sec1-tri1;
double angle2=acos((d*d+r*r-x*x)/(d*r*2))*2;
double tri2=r*r*sin(angle2)/2;
double sec2=angle2*r*r/2;
double sq2=sec2-tri2;
double ans=sq1+sq2;
return ans-s;
}
int main()
{
double x0,y0,x1,y1;
int T,i;
scanf("%d",&T);
while(T--)
{
scanf("%lf%lf%lf%lf%lf",&x0,&y0,&x1,&y1,&r);
s=pi*r*r/2;
d=dis(x0,y0,x1,y1);
tmp=r/sqrt(2);
if(tmp+d>r)
{
double L=d-r,R=d+r,m;
for(i=0;i<100;i++)
{
m=(L+R)/2;
if(calc(m)+eps>0) R=m;
else L=m;
}
printf("%.4f\n",L);
}
else printf("%.4f\n",tmp);
}
}
題目:點擊打開鏈接
題意:求出n個數中出現次數最多的數字,如果存在數量相同的數,從小到大輸出
分析:
因爲這些數不超過1000,所以用一個vis數組保存下標爲該數,出現的次數,每次輸入x時,vis[x]++,然後從1到1000,找數組的哪個vis值最大,令最大值爲mx,然後再從1到1000遍歷一遍,將vis[x]等於mx的x保存到ans答案數組中,cnt記錄答案的個數,最後將ans數組輸出,注意空格和變量的初始化。
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <map>
#define PR pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define sqr(x) ((x)*(x))
#define ll long long
const ll INF = (1ULL<<63)-1;
const int inf=0x3f3f3f3f;
const int M=100010;
const int N=100100;
const ll MOD=1000000007;
const double eps=1e-8;
const double pi=acos(-1.0);
using namespace std;
int a[N],n,vis[N],ans[N],cnt;
int main()
{
int T,i;
scanf("%d",&T);
while(T--)
{
memset(vis,0,sizeof(vis));
cnt=0;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
vis[a[i]]++;
}
int mx=0;
for(i=1;i<=1000;i++)
{
mx=max(mx,vis[i]);
}
for(i=1;i<=1000;i++)
{
if(vis[i]==mx) ans[cnt++]=i;
}
for(i=0;i<cnt;i++)
{
printf("%d",ans[i]);
if(i==cnt-1) puts("");
else printf(" ");
}
}
}
題目:點擊打開鏈接
題意:f (0) = b, f (1) = a,
f (2) = f (1) + f (0) = ab,
f (3) = f (2) + f (1) = aba,
f (4) = f (3) + f (2) = abaab,
......
求 f (n) 中的第 m 位是哪個字母
分析:
對於f[n],字符串的長度就是n對應的斐波那契值,我們可以先預處理出n<=90的斐波那契數列爲fb,fb都是在64位整數範圍內的。
對於n>=2時,對應的字符串都是前者連接上前前者,因此如果m的大小大於fb[n-1],下標爲n-2的字符串中也有對應的位置,對應的位置是m=m-f[n-1]。如果m的大小小於等於fb[n-1],那麼下標爲n-1的字符串也有對應的位置,對應的位置是m,因此可以這樣逆推下去,直到m等於1或者0,如果是1,那麼它就是a,如果是0就是b。
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <map>
#define PR pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define sqr(x) ((x)*(x))
#define ll unsigned long long
const ll INF = 1e18;
const int inf=0x3f3f3f3f;
const int M=18;
const int N=100;
const int MOD=1000000007;
const double eps=1e-10;
const double pi=acos(-1.0);
using namespace std;
ll f[N],m;
int n;
void init()
{
f[0]=f[1]=1;
for(int i=2;i<=90;i++) f[i]=f[i-1]+f[i-2];
}
int main()
{
init();
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%llu",&n,&m);
if(n==0) {puts("b");continue;}
for(int i=n;i>=3;i--)
{
if(m>f[i-1]) m-=f[i-1];
}
if(m==1) puts("a");
else puts("b");
}
}
題目:點擊打開鏈接
題意:開學第一天,萌新要排排坐喫果果啦,KI要求萌新們坐成一排。
現在萌新們必須要按KI的秩序表一個一個地就坐。
萌新中包括男孩子、女孩子和扶她。
男孩子會毫不猶豫地坐到當前已經坐好的人的後一個座位,女孩子在入座時會和前面的**男孩子**的隔一個座位坐,而扶她會觀察前面連續坐的人數,若人數大於等於心理容忍度$k$,那麼扶她會隔一個座位坐,否則直接坐到當前的後一個座位。
那麼問題來了,KI想知道至少需要多少把椅子,才能讓這些萌新正好坐成一排。
分析:
直接枚舉字符串的字符,ans表示一共需要多少把椅子,cnt表示在當前人坐之前已經有多少人連坐了,如果字符是a,那麼直接ans++,cnt++,如果字符是b,判斷他前面有沒有人,如果有人且是男的,需要多加一把椅子空一格cnt=1,ans+=2,否則直接ans++,cnt++,如果字符是c,判斷cnt的個數是否大於等於k,如果大於等於k,則需要多加一把椅子空一格cnt=1,ans+=2,否則直接ans++,cnt++。最後輸出ans即可
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <map>
#define PR pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define sqr(x) ((x)*(x))
#define ll unsigned long long
const ll INF = 1e18;
const int inf=0x3f3f3f3f;
const int M=18;
const int N=100100;
const int MOD=1000000007;
const double eps=1e-10;
const double pi=acos(-1.0);
using namespace std;
char a[N];
int main()
{
int T,n,k,i;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%s",&n,&k,a);
int ans=0,cnt=0;
for(i=0;a[i];i++)
{
if(a[i]=='a') ans++,cnt++;
else if(a[i]=='b')
{
if(i&&a[i-1]=='a') ans+=2,cnt=1;
else ans++,cnt++;
}
else
{
if(cnt>=k) ans+=2,cnt=1;
else ans++,cnt++;
}
}
printf("%d\n",ans);
}
}
J:萌新的旅行
題目:點擊打開鏈接
題意: zstu的萌新們準備去自助旅行,他們租了一輛吉普車,然後選擇了n個城市作爲遊覽地點。然後他們驚喜的發現他們選擇的城市剛好繞城一個環。
也就是說如果給所有城市按照0,1,2,……,n-1編號,0號城市和n-1號城市是相鄰的,並且只能從i號城市去(i+1)%n號城市。
已知每個城市可以充油gas(i),從 i 到 (i+1)%n 城市耗油 cost(i)。
假設這輛吉普車沒有的油箱一開始是空的,並且沒有上限。
沒有油的話自然就不能繼續旅行了,這個問題讓萌新們非常困擾。作爲優秀的acmer,請你幫他們找到一個出發城市,使得萌新們能遊覽儘可能多的城市(注意最多遊覽n個城市)。如果有多個可選擇的出發城市,那麼請把他們按照編號從小到大輸出。
分析:
因爲要連續的經過城市,gas必須大於cost,且運動是單向的,所以可以把一個點和右邊的邊縮成一個值,表示在這個到這個城市到下一個城市還有多少gas,下標從一開始,令a_i=gas_i-cost_i,因爲是環裝城市分佈,所以數組都開2*n,保證可以有一次循環。注意a_i表示的是城市i到達城市(i+1)時剩餘的油量,不是城市i的剩餘油量!
當所有的a_i都是小於等於零,即任何一點都不能到達下一個點,直接輸出所有的點即可。
其他情況時,一段路的花費可以用前綴和相減快速計算,所以把前綴和pre也記下來。從一個城市出發時,假設能到達一個城市時,油量小於等於原先的城市,兩者相減的油量小於等於零,故假設不成立,因此只要將點i向右遍歷,遇到一個pre_j<=pre_(i-1)(pre_j表示到達城市(j+1)的剩餘油量,pre_(i-1)表示從城市i出發時的初始油量),則說明已經走了儘可能遠的路了,其中走過的城市數目是爲(j-i)。
然後我們要考慮如何求出哪個最大的j,滿足pre_j>pre_(i-1)。因爲我們通過前綴和,求出了每達到一個城市的剩餘油量,我們可以用倍增法,預處理出從點i開始算起,走2^k個城市的過程中的油量最低值,然後可以枚舉每個點i,對路程長度進行二分,因爲路程最長爲n,所以令l=i,r=i+n,m=(l+r)/2,每次O(1)的算出從點i到點m的最低油量值,令最終能走到res城市,如果油量大於pre_(i-1),則說明還可以往右走,res=m,l=m+1,否則r=m-1,最後得到的(res-i+1)就是從點i出發,經過的城市數目,將一些個數和i保存一下,最後輸出最大個數的i即可。
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <map>
#define PR pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define sqr(x) ((x)*(x))
#define ll long long
const ll INF = 1e18;
const int inf=0x3f3f3f3f;
const int M=18;
const int N=100100;
const int MOD=1000000007;
const double eps=1e-9;
const double pi=acos(-1.0);
using namespace std;
int g[N*2],c[N*2],a[N*2];
int dp[N*2][18];
int pre[N*2];
int ans[N];
int vt[N];
int n,cnt;
int calc(int l,int r)
{
int k=log2(r-l+1);
return min(dp[l][k],dp[r-(1<<k)+1][k]);
}
int main()
{
int T,i,j;
scanf("%d",&T);
while(T--)
{
cnt=0;
memset(ans,0,sizeof(ans));
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&g[i]),g[i+n]=g[i];
for(i=1;i<=n;i++)scanf("%d",&c[i]),c[i+n]=c[i];
int ok=0;
for(i=1;i<=2*n;i++)
{
a[i]=g[i]-c[i];
dp[i][0]=dp[i-1][0]+a[i];
if(a[i]>0) ok=1;
}
if(ok==0)
{
for(i=0;i<n;i++)
{
printf("%d",i);
if(i==n-1) puts("");
else printf(" ");
}
continue;
}
for(j=1;j<18;j++)
for(i=1;i+(1<<j)-1<=n*2;i++)
dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
int mx=0;
for(i=1;i<=n;i++)
{
if(a[i]<=0) continue;
int l=i,r=i+n,res,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(calc(i,mid)<=dp[i-1][0]) r=mid-1;
else res=mid,l=mid+1;
}
ans[i]=res-i+1;
}
for(i=1;i<=n;i++) mx=max(mx,ans[i]);
for(i=1;i<=n;i++)
{
if(mx==ans[i]) vt[cnt++]=i-1;
}
for(i=0;i<cnt;i++)
{
printf("%d",vt[i]);
if(i==cnt-1) puts("");
else printf(" ");
}
}
}
K:KI的目標
題目:點擊打開鏈接
題意:KI給自己制定了最近制定了一些學習目標,因爲有些大目標的達到要先完成一些小目標,所以KI就下意識的把這些目標連成了一棵樹,以1號目標爲根。
KI是個很謹慎的人,於是他請他的朋友們對這棵樹上的每條邊評估了一個努力值cost(i),並對每個目標評估了一個
價值val(i)。
然後KI決定去掉樹上的一些不可行的目標,他判斷的依據是:
假設目標v屬於以u爲根的子樹,如果dis(u,v)<val(u)-val(v),那麼以v爲根的整棵子樹都會被去掉。(dis(u,v)從節點u到節點v所有邊的邊權和)
請幫KI計算一下最後他還剩下幾個目標。
分析:
首先可以用鏈式前向星保存每條邊的兩節點編號和權值cost,因爲輸入時不知道是父節點還是子節點,所以要存兩條邊。
我們可以計算發現,如果存在一個u,滿足dis(u,v)<val(u)-val(v),val(v)<val(u)-dis(u,v)就要要刪掉v的子樹,因此可以進行貪心,val[v]要滿足大於等於val(u)-dis(u,v)的最大值。遍歷每一個節點時保存最大的“val(u)”,這個val(u)並不是真正的節點的val值,而是從根節點開始遍歷下來直到v的父節點時,路徑上val的值減去cost的最大值,即val(u)-dis(u,v)的最大值。
因此建完樹以後,從1開始深搜,dfs保存當前節點u,父節點fa和當前的“val(u)”記爲sum。每次遍歷到一個節點時,判斷sum-cost和val[v]哪個大,每次都取大值爲sum,就保證了sum是路徑上val的值減去cost的最大值。如果sum<=val[u],那麼這個點就是合法的,答案個數ans++,否則返回上一層。遍歷完所有可能的點後,最後輸出ans即可
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#include <map>
#define PR pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define sqr(x) ((x)*(x))
#define ll long long
const ll INF = 1e18;
const int inf=0x3f3f3f3f;
const int M=18;
const int N=101000;
const int MOD=1000000007;
const double eps=1e-9;
const double pi=acos(-1.0);
using namespace std;
struct Edge
{
int v,nxt;
ll cost;
}edge[N*2];
int head[N],cnt;
void addedge(int u,int v,ll w)
{
edge[cnt].v=v,edge[cnt].cost=w,edge[cnt].nxt=head[u],head[u]=cnt++;
edge[cnt].v=u,edge[cnt].cost=w,edge[cnt].nxt=head[v],head[v]=cnt++;
}
int n,u,v,ans;
ll w,val[N];
void dfs(int u,int fa,ll sum)
{
if(sum<=val[u]) ans++;
else return;
for(int i=head[u];i!=-1;i=edge[i].nxt)
{
int v=edge[i].v;
if(fa!=v)
{
ll tmp=sum-edge[i].cost;
dfs(v,u,max(tmp,val[v]));
}
}
}
int main()
{
int T,i;
scanf("%d",&T);
while(T--)
{
memset(head,-1,sizeof(head));
cnt=0;
scanf("%d",&n);
for(i=1;i<n;i++)
{
scanf("%d%d%lld",&u,&v,&w);
addedge(u,v,w);
}
for(i=1;i<=n;i++)
{
scanf("%lld",&val[i]);
}
ans=0;
dfs(1,-1,val[1]);
printf("%d\n",ans);
}
}