2021牛客暑期多校訓練營2

比賽連接:https://ac.nowcoder.com/acm/contest/11253ios

第一場有事沒打;第二場暑訓正式開始。C,D,F,K,4/12,墊底了,咳。G記錄得比較詳細,我就不回憶了。c++

改了兩天題:數組

A:Arithmetic Progressionide

題意:優化

給一個序列 \( a \) ,求其全部知足 排序後是等差數列 的子區間的個數。\( 1 \leq n \leq 10^5 \) , \( 1 \leq a_i \leq 10^9 \) , \( a_i \) 各不相同。ui

分析:spa

快速斷定子區間 \( \left [ l,r \right ) \) 知足條件的方法:若排序後爲等差數列,則公差 \( d = gcd\left ( b_{l+1}-b_1 , b_{l+2}-b_{l+1} , ... , b_r-b_{r-1} \right ) \) ;code

那麼斷定條件就能夠是 \( max\left \{ a_l , ... , a_r \right \} - min\left \{ a_l , ... , a_r \right \} = ( r-l ) * gcd\left ( b_{l+1}-b_l , b_{l+2}-b_{l+1} , ... , b_r-b_{r-1} \right ) \) ,並且能夠發現 \( max\left \{ a_l , ... , a_r \right \} - min\left \{ a_l , ... , a_r \right \} \) 必定大於等於 \( ( r-l ) * gcd\left ( b_{l+1}-b_l , b_{l+2}-b_{l+1} , ... , b_r-b_{r-1} \right ) \) ;排序

因此能夠考慮枚舉 \( r \) ,再 \( log(n) \) 維護\( max \) , \( min \) 和 \( gcd \);隊列

上式寫成 \( max\left \{ a_l , ... , a_r \right \} - min\left \{ a_l , ... , a_r \right \} + l * gcd = r * gcd \) ,用線段樹來維護 \( max\left \{ a_l , ... , a_r \right \} - min\left \{ a_l , ... , a_r \right \} + l * gcd \) 的最小值以及是這個最小值的點的個數;\( max \) 和 \( min \) 能夠結合單調棧維護,進行區間修改;\( gcd \) 直接單點修改,可是對於每一個 \( l \) ,這個點上存的 \( gcd \) (初值 \( a_{l+1}-a_l\) )最多修改 \( log(n) \) 次,因此複雜度是 \( nlog(n) \);每一個 \( r \) 處找一遍答案,也就是看看最小值和上式右面是否相等,相等的話就把答案加到個數裏。

代碼以下:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=
#include<iostream>
#define ll long long
#define ls (u<<1)
#define rs (u<<1|1)
#define mid ((l+r)>>1)
using namespace std;
int const N=1e5+5;
ll const INF=1e17;
int n,tpmn,tpmx,cnt;
ll a[N],qmn[N],qmx[N],g[N],gpos[N],ans;
struct Nd{
    ll s,tg,cnt,mn;
}tr[N<<2];
struct qNd{
    ll mn,num;
};
ll gcd(ll a,ll b){return !b?a:gcd(b,a%b);}
ll ab(ll x){return x>0?x:-x;}
void pdn(int u)
{
    if(!tr[u].tg)return;
    ll s=tr[u].tg; tr[u].tg=0;
    tr[ls].tg+=s; tr[ls].mn+=s;
    tr[rs].tg+=s; tr[rs].mn+=s;
}
void upt(int u)
{
    //printf("upt(%d):ls.mn=%lld rs.mn=%lld\n",u,tr[ls].mn,tr[rs].mn);
    if(tr[ls].mn<tr[rs].mn)tr[u].mn=tr[ls].mn,tr[u].cnt=tr[ls].cnt;
    if(tr[ls].mn==tr[rs].mn)tr[u].mn=tr[ls].mn,tr[u].cnt=tr[ls].cnt+tr[rs].cnt;
    if(tr[ls].mn>tr[rs].mn)tr[u].mn=tr[rs].mn,tr[u].cnt=tr[rs].cnt;
}
void init(int u,int l,int r)
{
    tr[u].mn=INF; tr[u].tg=0; tr[u].cnt=r-l+1;
    if(l==r)return;
    //printf("u=%d ls=%d rs=%d l=%d r=%d mid=%d\n",u,ls,rs,l,r,mid);
    init(ls,l,mid); init(rs,mid+1,r);
}
void add(int u,int l,int r,int ql,int qr,ll s)
{
    //printf("add(u=%d l=%d r=%d ql=%d qr=%d s=%lld\n",u,l,r,ql,qr,s);
    if(l>=ql&&r<=qr){tr[u].tg+=s; tr[u].mn+=s; return;}
    pdn(u);
    if(mid>=ql)add(ls,l,mid,ql,qr,s);
    if(mid<qr)add(rs,mid+1,r,ql,qr,s);
    upt(u);
    //printf("upt:u=%d l=%d r=%d mn=%d cnt=%d\n",u,l,r,tr[u].mn,tr[u].cnt);
}
qNd qry(int u,int l,int r,int ql,int qr)
{
    //printf("qry:u=%d l=%d r=%d ql=%d qr=%d\n",u,l,r,ql,qr);
    if(l>r||ql>qr)return (qNd){-1,0};
    if(l>=ql&&r<=qr)return (qNd){tr[u].mn,tr[u].cnt};
    //printf("qry:pdn(%d)\n",u);
    pdn(u);
    if(mid>=qr)return qry(ls,l,mid,ql,qr);
    if(mid<ql)return qry(rs,mid+1,r,ql,qr);
    //upt(u);
    qNd qls=qry(ls,l,mid,ql,qr),qrs=qry(rs,mid+1,r,ql,qr),ret=qls;
    if(qls.mn>qrs.mn)ret=qrs;
    else if(qls.mn==qrs.mn)ret.num+=qrs.num;
    return ret;
}
int main()
{
    int T; scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n); init(1,1,n); ans=0; cnt=0; tpmn=0; tpmx=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]); //printf("i=%d\n",i);
            while(tpmn&&a[qmn[tpmn]]>=a[i])add(1,1,n,qmn[tpmn-1]+1,qmn[tpmn],a[qmn[tpmn]]),tpmn--;
            add(1,1,n,qmn[tpmn]+1,i,-a[i]); qmn[++tpmn]=i;
            while(tpmx&&a[qmx[tpmx]]<=a[i])add(1,1,n,qmx[tpmx-1]+1,qmx[tpmx],-a[qmx[tpmx]]),tpmx--;
            add(1,1,n,qmx[tpmx]+1,i,a[i]); qmx[++tpmx]=i;
            if(i==1)continue;//
            g[++cnt]=ab(a[i]-a[i-1]); gpos[cnt]=i-1;
            add(1,1,n,i-1,i-1,(ll)(i-1)*g[cnt]-INF);//-INF!
            for(int k=cnt-1;k;k--)
                if(g[k+1]%g[k])
                {
                    ll pre=g[k]; g[k]=gcd(g[k+1],g[k]);//
                    for(int p=gpos[k];p<gpos[k+1];p++)add(1,1,n,p,p,(ll)p*(g[k]-pre));
                }
            //printf("cnt=%d\n",cnt);
            //for(int i=1;i<=cnt;i++)printf("g[%d]=%d ",i,g[i]); printf("\n");
            int num=0;
            for(int i=1;i<=cnt;i++)
                if(g[i]!=g[i-1])g[++num]=g[i],gpos[num]=gpos[i];
            cnt=num;
            //printf("cnt2=%d\n",cnt);
            //for(int i=1;i<=cnt;i++)printf("g[%d]=%d ",i,g[i]); printf("\n");
            for(int k=1;k<=cnt;k++)//一個,兩個數也是
            {
                //printf("gpos[%d]=%d\n",k,gpos[k]);
                qNd t=qry(1,1,n,gpos[k],(k==cnt)?i-1:gpos[k+1]-1);
                //printf("qry.mn=%lld qry.num=%lld i*g=%lld\n",t.mn,t.num,(ll)i*g[k]);
                if(t.mn==(ll)i*g[k])ans+=t.num;
            }
        }
        printf("%lld\n",ans+n);
    }
    return 0;
}
me

 

C:Draw Grids

題意:

給定一個 \( n * m \) 的點陣,每次選兩個相鄰點連線,不能連出封閉圖形,兩人輪流操做,不能操做者輸。

分析:

不能造成環,因此終態是一棵樹,因此能夠根據點數的奇偶性判斷。

簽到題,Y作了,G寫了。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=
#include<bits/stdc++.h>
using namespace std;
int n,m;
int main()
{   scanf("%d%d",&n,&m);
    if((n==1||n==3)&&(m==1||m==3)) puts("NO");
    else puts("YES");
}
Y&G

 

D:Er Ba Game

題意+分析:

按照題意模擬便可。

簽到題,G寫了。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=
#include<bits/stdc++.h>
using namespace std;
int T,A1,B1,A2,B2,Flag;
int main()
{   for(scanf("%d",&T);T;T--)
    {   scanf("%d%d%d%d",&A1,&B1,&A2,&B2),Flag=0;
        if(A1>B1) swap(A1,B1);
        if(A2>B2) swap(A2,B2);
        if(A1==A2&&B1==B2&&!Flag) puts("tie"),Flag=1;
        if(A1==2&&B1==8&&!Flag) puts("first"),Flag=1;
        if(A2==2&&B2==8&&!Flag) puts("second"),Flag=1;
        if(A1==B1&&A2==B2&&A1>A2&&!Flag) puts("first"),Flag=1;
        if(A2==B2&&A1==B1&&A2>A1&&!Flag) puts("second"),Flag=1;
        if(A1==B1&&A2!=B2&&!Flag) puts("first"),Flag=1;
        if(A2==B2&&A1!=B1&&!Flag) puts("second"),Flag=1;
        if((A1+B1)%10>(A2+B2)%10&&!Flag) puts("first"),Flag=1;
        if((A2+B2)%10>(A1+B1)%10&&!Flag) puts("second"),Flag=1;
        if(B1>B2&&!Flag) puts("first"),Flag=1;
        if(B2>B1&&!Flag) puts("second"),Flag=1;
    }
}
G

 

F:Girlfriend

題意:

空間內有六個點:定點 \( A, B, C, D \) ,動點 \( P_1, P_2 \) ,知足 \( |P_1A| \geq k_1|P_1B| , |P_2C| \geq k_2|P_2D| \) , 求 \( P_1, P_2 \) 各自活動區域的交的體積。

分析:

容易發現 \( P_1, P_2 \) 各自的活動區域都是一個球(阿波羅尼斯球),能夠求出球心座標和半徑;因此就是求兩個球相交部分的體積。

能夠分紅兩邊來算;先用餘弦定理求出球心到兩球切面的距離,再積分一下便可。

Y和G寫了。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=
#include<bits/stdc++.h>
#define N 200010
using namespace std;
const double pi=acos(-1);

int T;
double X1,X2,X3,X4,X5,Y1,Y2,Y3,Y4,Y5,z1,z2,z3,z4,z5,r1,r2,d,k1,k2,X6,Y6,z6,ans;
double read()
{   int a=0,c=1;   char b=getchar();
    while(b!='-'&&b<'0'||b>'9') b=getchar();
    if(b=='-') c=-1,b=getchar();
    while(b>='0'&&b<='9') a=a*10+b-48,b=getchar();
    return a*c*1.0;
}

double ask(double x, double y){
    return pi*(y*y*(y-x)-y*y*y/3+x*x*x/3);
}
int main(){
    scanf("%d",&T);
    while(T--){
        X1=read(),Y1=read(),z1=read();
        X2=read(),Y2=read(),z2=read();
        X3=read(),Y3=read(),z3=read();
        X4=read(),Y4=read(),z4=read();
        k1=read(),k2=read();
        X5=(X2*k1*k1-X1)/(k1*k1-1);
        Y5=(Y2*k1*k1-Y1)/(k1*k1-1);
        z5=(z2*k1*k1-z1)/(k1*k1-1);
        X6=(X4*k2*k2-X3)/(k2*k2-1);
        Y6=(Y4*k2*k2-Y3)/(k2*k2-1);
        z6=(z4*k2*k2-z3)/(k2*k2-1);
        r1=sqrt( ((X2*k1*k1-X1)*(X2*k1*k1-X1)+(k1*k1-1)*(X1*X1-k1*k1*X2*X2))*1.0/((k1*k1-1)*(k1*k1-1)) +
                 ((Y2*k1*k1-Y1)*(Y2*k1*k1-Y1)+(k1*k1-1)*(Y1*Y1-k1*k1*Y2*Y2))*1.0/((k1*k1-1)*(k1*k1-1)) +
                 ((z2*k1*k1-z1)*(z2*k1*k1-z1)+(k1*k1-1)*(z1*z1-k1*k1*z2*z2))*1.0/((k1*k1-1)*(k1*k1-1)) );
        r2=sqrt( ((X4*k2*k2-X3)*(X4*k2*k2-X3)+(k2*k2-1)*(X3*X3-k2*k2*X4*X4))*1.0/((k2*k2-1)*(k2*k2-1)) +
                 ((Y4*k2*k2-Y3)*(Y4*k2*k2-Y3)+(k2*k2-1)*(Y3*Y3-k2*k2*Y4*Y4))*1.0/((k2*k2-1)*(k2*k2-1)) +
                 ((z4*k2*k2-z3)*(z4*k2*k2-z3)+(k2*k2-1)*(z3*z3-k2*k2*z4*z4))*1.0/((k2*k2-1)*(k2*k2-1)) );
        d=sqrt((X5-X6)*(X5-X6) + (Y5-Y6)*(Y5-Y6) + (z5-z6)*(z5-z6));
        if(d>r1+r2){
            ans=0.0;
        }else{
            double mi=r1<r2?r1:r2;
            double ma=r1>r2?r1:r2;
            if(d+mi<ma){
                ans=4.0/3*pi*mi*mi*mi;
            }
            else{
                double h=(r1*r1-r2*r2+d*d)*1.0/(2*d);
                //ans=2*(4.0/3*pi*r1*r1*r1*2*acos(h/r1)/(2*pi) - pi*(r1*r1-h*h)*h/3);
                //ans=(4.0/3*pi*r1*r1*r1*2*acos(h/r1)/(2*pi) - pi*(r1*r1-h*h)*h/3)+(4.0/3*pi*r2*r2*r2*2*acos((d-h)/r2)/(2*pi) - pi*(r2*r2-(d-h)*(d-h))*(d-h)/3);
                ans=ask(h,r1)+ask(d-h,r2);
            }
        }
        printf("%.3lf\n",ans);
    }
    return 0;
}
Y&G

 

G:League of Legends

題意:

給定 \( n \) 個區間 \( \left [ a_i , b_i \right ) \),要求將它們分紅 k 組,最大化每組交的長度之和。\( 1 \leq k \leq n \leq 5000 \) , \( 1 \leq a < b < 10^5 \) 。

分析:

對於內部徹底包含某區間的一個大區間,其最優決策有兩種:1.和它包含的那個區間同組,不影響答案;2.本身單獨一組。想緣由的話,能夠把大區間當作最後一個加入的區間(反正沒有順序);它加入之後若是要組隊,答案不增,因此最優的方案就是讓答案不變,因此一種最優的組隊方法就是和包含的那個區間同組。

而後就能夠去掉這些大區間了,它們的貢獻最後適當加一下便可。

如今剩下的區間就都是首尾相交的了,能夠左端點遞增排個序,而後DP, \( f[i][j] \) 表示前 \( i \) 個區間分了 \( j \) 組;那麼轉移的時候須要找一個地方分出最後一組,這裏就能夠用單調隊列優化了。(可是連這也忘了因此專門看了看單調隊列優化DP囧)

而後時間複雜度是\( O(n^2) \)。

代碼以下:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int const N=5005;
int n,bm,m,K,b[N],f[N][N],q[N],ans;
struct Nd{
    int l,r;
    bool operator < (const Nd a) const{
        return l!=a.l?l<a.l:r>a.r;
    }
}a[N];
bool cmp(int a,int b){return a>b;}
int main()
{
    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i].l,&a[i].r);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
    {
        bool fl=0;
        for(int j=i+1;j<=n;j++)
            if(a[j].r<=a[i].r){b[++bm]=a[i].r-a[i].l; fl=1; break;}
        if(!fl)a[++m]=a[i];
    }
    sort(b+1,b+bm+1,cmp);
    for(int i=2;i<=bm;i++)b[i]+=b[i-1];
    memset(f,255,sizeof f);
    f[0][0]=0;
    for(int j=1;j<=K;j++)
    {
        int hd=1,tl=0;
        for(int i=1;i<=m;i++)
        {
            if(f[i-1][j-1]>=0)//
            {
                while(hd<=tl&&f[q[tl]][j-1]+a[q[tl]+1].r<=f[i-1][j-1]+a[i].r)tl--;
                q[++tl]=i-1;
            }
            while(hd<=tl&&a[q[hd]+1].r<=a[i].l)hd++;//
            if(hd<=tl)f[i][j]=f[q[hd]][j-1]+a[q[hd]+1].r-a[i].l;//
        }
        if(bm>=K-j&&f[m][j]>=0)ans=max(ans,f[m][j]+b[K-j]);//
        //printf("f[%d][%d]=%d ans=%d\n",m,j,f[m][j],ans);
    }
    printf("%d\n",ans);
    return 0;
}
me

 

I:Penguins

題意:

給出左右兩張 \( 20*20 \) 的圖(有平地和障礙),兩隻企鵝分別從指定起點出發去指定終點,並且它們的走法是鏡像的。輸出最短步數、走法和有路徑的圖。

分析:

就是BFS。但我在比賽時沒寫好存儲路徑的地方。G改了半天,沒改好。而後這題沒過。

後來G改好了。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=
#include<bits/stdc++.h>
using namespace std;
const int MAXN=22;
struct POS{
    int X1,Y1,X2,Y2,K;
    POS operator + (POS S)
    {   return (POS){X1+S.X1,Y1+S.Y1,X2+S.X2,Y2+S.Y2,K+S.K};   }
    bool operator == (POS S)
    {   return X1==S.X1&&X2==S.X2&&Y1==S.Y1&&Y2==S.Y2&&K==S.K;   }
}Last[MAXN][MAXN][MAXN][MAXN],Zero,St,End,L,R,U,D;
char M1[MAXN][MAXN],M2[MAXN][MAXN],Ans[500];
int As;
queue<POS>Team;
void Prepare()
{   L=(POS){0,-1,0,1,'L'},R=(POS){0,1,0,-1,'R'};
    U=(POS){-1,0,-1,0,'U'},D=(POS){1,0,1,0,'D'};
    St=(POS){20,20,20,1,0},End=(POS){1,20,1,1,0};
}
POS Walk(POS Now,POS Dir)
{   POS New=Now+Dir;
    if(M1[New.X1][New.Y1]=='#') New.X1=Now.X1,New.Y1=Now.Y1;
    if(M2[New.X2][New.Y2]=='#') New.X2=Now.X2,New.Y2=Now.Y2;
    New.K=0;
    POS &Lp=Last[New.X1][New.Y1][New.X2][New.Y2];
    if(Lp==Zero) Lp=Now,Lp.K=Dir.K,Team.push(New);
    return New;
}
void Bfs()
{   Team.push(St),Last[20][20][20][1]=St;
    for(POS Now;!Team.empty();)
    {   Now=Team.front(),Team.pop();
        if(Walk(Now,D)==End) return ;
        if(Walk(Now,L)==End) return ;
        if(Walk(Now,R)==End) return ;
        if(Walk(Now,U)==End) return ;
    }
}
void Print()
{   POS Now=End;
    while(!(Now==St))
        M1[Now.X1][Now.Y1]=M2[Now.X2][Now.Y2]='A',Now=Last[Now.X1][Now.Y1][Now.X2][Now.Y2],Ans[++As]=Now.K;
    printf("%d\n",--As);
    for(int i=As;i>=1;i--) putchar(Ans[i]);
    puts(""),M1[St.X1][St.Y1]=M2[St.X2][St.Y2]='A';
    for(int i=1;i<=20;i++)
    {   for(int j=1;j<=20;j++) putchar(M1[i][j]);
        putchar(' ');
        for(int j=1;j<=20;j++) putchar(M2[i][j]);
        puts("");
    }
}
int main()
{   Prepare();
    for(int i=1;i<=20;i++) cin>>M1[i]+1>>M2[i]+1;
    for(int i=0;i<=21;i++) M1[i][0]=M1[i][21]=M1[0][i]=M1[21][i]=M2[i][0]=M2[i][21]=M2[0][i]=M2[21][i]='#';
    Bfs(),Print();
}
G

 

K:Stack

題意:

已知若干時刻單調棧的大小,構造一個合法的原序列。\( n \leq 10^6 \) 。

分析:

這題比賽時我曾想用差分、前綴和之類的東西。反正G寫了鏈表而後過了。

看了看,寫得真棒。而後我模仿了。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=
#include<iostream>
#include<cstring>
using namespace std;
int const N=1e6+5;
int n,m,a[N],b[N],top,stk[N],pre[N],aft[N];
int main()
{
    scanf("%d%d",&n,&m); memset(b,255,sizeof b);
    for(int i=1,p,x;i<=m;i++)
        scanf("%d%d",&p,&x),b[p]=x;
    if(b[1]!=-1&&b[1]!=1){printf("-1\n"); return 0;}
    stk[++top]=1; aft[0]=1; pre[1]=0; aft[1]=n+1; pre[n+1]=1;//此處角標和值都是位置
    for(int i=2;i<=n;i++)//i是位置
    {
        if(b[i]==-1)
        {
            pre[i]=stk[top]; aft[i]=aft[stk[top]]; aft[stk[top]]=i; pre[aft[i]]=i;
            stk[++top]=i;
        }
        else
        {
            if(top<b[i]-1){printf("-1\n"); return 0;}
            int t=stk[b[i]-1];
            aft[i]=aft[t]; pre[i]=t; pre[aft[t]]=i; aft[t]=i;
            top=b[i]-1; stk[++top]=i;
        }
    }
    int nw=aft[0];
    for(int i=1;i<=n;i++)//i是值
        a[nw]=i,nw=aft[nw];
    for(int i=1;i<=n;i++)printf("%d ",a[i]); printf("\n");
    return 0;
}
me

 

 

L:Wechat Walk

題意:

\( n \) 我的,\( m \) 條無向邊表示他們的好友關係,總時間 \( q \) ;每一個時刻只有一我的走了若干步;若是一我的某一時刻在其全部直接好友中步數最多,則其這一時刻是冠軍;問每一個人是冠軍的總時長。\( n , m , q \leq 2*10^5 \)。

分析:

一開始覺得朋友的朋友也是朋友,後來發現不是,呵呵。

能夠分類處理,把朋友個數 \( > \sqrt{n} \) 的人和朋友個數 \( \leq \sqrt{n} \) 的人分開處理,分別稱做「大點」和「小點」;一個是人數少,一個是朋友少,就好辦了;

用一個數組記錄每一個人的總步數;每一個點開一個 vector,預處理存本身的大點朋友;每一個大點本身開一個小根堆,裏面是本身的全部朋友,按步數排序;再用一個數組記錄每一個人進入冠軍狀態的時刻;

若是當前步數增長的是小點,直接遍歷它的朋友,修改冠軍,而後(再次)把本身放進大點朋友的堆裏;

若是當前步數增長的是大點,從堆裏提取朋友,修改它們的冠軍(這裏要判斷一下提取出的信息是否過時了),再修改本身的冠軍狀態,而後(再次)把本身放進大點朋友的堆裏;

最後輸出答案。

代碼以下:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=
#include<iostream>
#include<vector>
#include<queue>
#include<cmath>
#define pb push_back
using namespace std;
int const N=2e5+5;
int n,m,tim,id[N],cnt,ans[N],du[N],sum[N],lst[N];
vector<int>to[N],to2[N];
struct Nd{
    int id,s;
    bool operator < (const Nd a) const{
        return s>a.s;
    }
};
priority_queue<Nd>q[N];
int main()
{
    scanf("%d%d%d",&n,&m,&tim);
    for(int i=1,x,y;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        to[x].pb(y); to[y].pb(x);
        du[x]++; du[y]++;
    }
    for(int i=1;i<=n;i++)
        if(du[i]>sqrt(n))id[i]=++cnt;
    for(int i=1;i<=n;i++)
        for(int j=0,v,sz=to[i].size();j<sz;j++)
            if(id[v=to[i][j]]) to2[i].pb(v);
    for(int t=1,u,s;t<=tim;t++)
    {
        scanf("%d%d",&u,&s); sum[u]+=s;
        if(!id[u])
        {
            bool fl=1;
            for(int i=0,v,sz=to[u].size();i<sz;i++)
            {
                if(sum[v=to[u][i]]>=sum[u])
                {
                    fl=0;
                    if(lst[u])ans[u]+=t-lst[u],lst[u]=0;
                }
                if(sum[u]>=sum[v]&&lst[v])ans[v]+=t-lst[v],lst[v]=0;
            }
            if(fl&&!lst[u])lst[u]=t;
            for(int i=0,v,sz=to2[u].size();i<sz;i++)
                if(sum[u]>=sum[v=to2[u][i]])q[v].push((Nd){u,sum[u]});
        }
        else
        {
            bool fl=1;
            while(q[u].size())
            {
                int v=q[u].top().id,ss=q[u].top().s;
                if(ss<sum[v]){q[u].pop(); continue;}
                if(ss>=sum[u])
                {
                    fl=0;
                    if(lst[u])ans[u]+=t-lst[u],lst[u]=0;
                }
                if(ss>sum[u])break;
                if(ss<=sum[u])
                {
                    q[u].pop();
                    if(lst[v])ans[v]+=t-lst[v],lst[v]=0;
                }
            }
            if(fl&&!lst[u])lst[u]=t;
            for(int i=0,v,sz=to2[u].size();i<sz;i++)
                if(sum[u]>=sum[v=to2[u][i]])q[v].push((Nd){u,sum[u]});
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(lst[i])ans[i]+=tim-lst[i];
        printf("%d\n",ans[i]);
    }
    return 0;
}
me
相關文章
相關標籤/搜索