浙江理工大學zstu2016新生賽題解

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);
    }
}
D:聖盃戰爭

題目:點擊打開鏈接
題意: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);
        }
    }
}

E:校慶

題目:點擊打開鏈接

題意:輸入一個日期,判斷它離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));
    }
}
F:牛喫草

題目:點擊打開鏈接

題意:農夫有一個長滿草的(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);
    }
}
G:衆數

題目:點擊打開鏈接

題意:求出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(" ");
        }
    }
}
H:KI的斐波那契

題目:點擊打開鏈接

題意: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");
    }
}
I:萌新喫果果

題目:點擊打開鏈接

題意:開學第一天,萌新要排排坐喫果果啦,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);
    }
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章