比賽連接:https://ac.nowcoder.com/acm/contest/11253ios
第一場有事沒打;第二場暑訓正式開始。C,D,F,K,4/12,墊底了,咳。G記錄得比較詳細,我就不回憶了。c++
改了兩天題:數組
題意:優化
給一個序列 \( 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 \) 處找一遍答案,也就是看看最小值和上式右面是否相等,相等的話就把答案加到個數裏。
代碼以下:
#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; }
題意:
給定一個 \( n * m \) 的點陣,每次選兩個相鄰點連線,不能連出封閉圖形,兩人輪流操做,不能操做者輸。
分析:
不能造成環,因此終態是一棵樹,因此能夠根據點數的奇偶性判斷。
簽到題,Y作了,G寫了。
#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"); }
題意+分析:
按照題意模擬便可。
簽到題,G寫了。
#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; } }
題意:
空間內有六個點:定點 \( 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寫了。
#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; }
題意:
給定 \( 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) \)。
代碼以下:
#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; }
題意:
給出左右兩張 \( 20*20 \) 的圖(有平地和障礙),兩隻企鵝分別從指定起點出發去指定終點,並且它們的走法是鏡像的。輸出最短步數、走法和有路徑的圖。
分析:
就是BFS。但我在比賽時沒寫好存儲路徑的地方。G改了半天,沒改好。而後這題沒過。
後來G改好了。
#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(); }
題意:
已知若干時刻單調棧的大小,構造一個合法的原序列。\( n \leq 10^6 \) 。
分析:
這題比賽時我曾想用差分、前綴和之類的東西。反正G寫了鏈表而後過了。
看了看,寫得真棒。而後我模仿了。
#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; }
題意:
\( n \) 我的,\( m \) 條無向邊表示他們的好友關係,總時間 \( q \) ;每一個時刻只有一我的走了若干步;若是一我的某一時刻在其全部直接好友中步數最多,則其這一時刻是冠軍;問每一個人是冠軍的總時長。\( n , m , q \leq 2*10^5 \)。
分析:
一開始覺得朋友的朋友也是朋友,後來發現不是,呵呵。
能夠分類處理,把朋友個數 \( > \sqrt{n} \) 的人和朋友個數 \( \leq \sqrt{n} \) 的人分開處理,分別稱做「大點」和「小點」;一個是人數少,一個是朋友少,就好辦了;
用一個數組記錄每一個人的總步數;每一個點開一個 vector,預處理存本身的大點朋友;每一個大點本身開一個小根堆,裏面是本身的全部朋友,按步數排序;再用一個數組記錄每一個人進入冠軍狀態的時刻;
若是當前步數增長的是小點,直接遍歷它的朋友,修改冠軍,而後(再次)把本身放進大點朋友的堆裏;
若是當前步數增長的是大點,從堆裏提取朋友,修改它們的冠軍(這裏要判斷一下提取出的信息是否過時了),再修改本身的冠軍狀態,而後(再次)把本身放進大點朋友的堆裏;
最後輸出答案。
代碼以下:
#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; }