JOISC 2014 Day1
阿新 • • 發佈:2021-10-30
感覺JOISC的題目要麼很經典,要麼很妙,所以特意來刷一下。
「JOISC 2014 Day1」巴士走讀
題目大意
給定\(m\)個條件,每個條件形如\(X\)時刻在\(A\)點能在\(Y\)時刻到\(B\)點,\(Q\)次詢問,當到達\(n\)點的時刻不超過\(T_i\)時,最晚可以在多少時間到\(1\)點。
題解
考慮到時刻的種類數不超過\(m\)種,因為肯定是第一個點有一條邊開通了然後對其造成了影響,所以可以處理出\(m\)種然後每次查詢二分。
我們考慮時間降序加入\(1\)號點的出邊。並對於每個點維護當前能最早什麼時候到。
每加入一條邊,我們就看能不能更新To的答案,如果能更新,那麼入隊並在To這個節點看有沒有能繼續走的邊,在事先對每個點出邊的出發時間排好序的情況下容易發現每條邊只會被走一次,時間複雜度\(O(mlogm+n+Qlogm)\)
code:
using namespace std; int n,m,W[N+5],X,Y,a,b,H[N+5],A[N+5],B[N+5],Th;struct Ques{int A,To,B;}; vector<Ques> F[N+5];I bool cmp(Ques x,Ques y){return x.A<y.A;}queue<int> Q; int main(){ freopen("1.in","r",stdin); RI i;scanf("%d%d",&n,&m);while(m--) scanf("%d%d%d%d",&a,&b,&X,&Y),F[a].push_back((Ques){X,b,Y}); for(i=1;i<=n;i++) sort(F[i].begin(),F[i].end(),cmp),H[i]=F[i].size()-1;Me(W,0x3f); while(~H[1]){ A[++Th]=W[1]=F[1][H[1]].A;Q.push(1);while(!Q.empty()) { X=Q.front();Q.pop();while(~H[X]&&F[X][H[X]].A>=W[X]) W[F[X][H[X]].To]>F[X][H[X]].B&&(Q.push(F[X][H[X]].To),W[F[X][H[X]].To]=F[X][H[X]].B),H[X]--; }B[Th]=W[n]; }for(i=1;i<=Th/2;i++) swap(A[i],A[Th-i+1]),swap(B[i],B[Th-i+1]);A[0]=-1; scanf("%d",&m);while(m--) scanf("%d",&X),printf("%d\n",A[upper_bound(B+1,B+Th+1,X)-B-1]); }
「JOISC 2014 Day1」有趣的家庭菜園
顯然這個東西考慮從大往小。
手玩一下發現其實最終序列是一段單調不降,再一段單調不升的東西。
從大到小後顯然一個點只能放到當前段的最開頭和最末尾。
然後算一下能造成多少逆序對貢獻兩邊貪心一下就好了。
code:
int n,m,A[N+5],Ns[N+5],Nh;ll Ans,F1,F2;vector<int> F[N+5]; namespace Tree{ int F[N+5];I void Get(int x){while(x<=n) F[x]++,x+=x&-x;} I int Find(int x){int Ans=0;while(x) Ans+=F[x],x-=x&-x;return Ans;} } int main(){ freopen("1.in","r",stdin); RI i,j;scanf("%d",&n);for(i=1;i<=n;i++) scanf("%d",&A[i]),Ns[++Nh]=A[i];sort(Ns+1,Ns+Nh+1);Nh=unique(Ns+1,Ns+Nh+1)-Ns-1; for(i=1;i<=n;i++) A[i]=lower_bound(Ns+1,Ns+Nh+1,A[i])-Ns,F[A[i]].push_back(i); for(i=Nh;i;i--){ for(j=0;j<F[i].size();j++) F1=Tree::Find(n)-Tree::Find(F[i][j]),F2=Tree::Find(F[i][j]),Ans+=min(F1,F2); for(j=0;j<F[i].size();j++) Tree::Get(F[i][j]);//printf("%d %lld %d %d\n",i,Ans,F1,F2); }printf("%lld\n",Ans); }
「JOISC 2014 Day1」歷史研究
似乎是一個回滾莫隊裸題來著。
程式碼太醜了不貼了。
「JOISC 2014 Day1」拉麵比較
記得這個東西大概CSP-S初賽的時候還考過。
首先顯然有一個\(2n-2\)次的兩個分別掃一遍的做法。
我們對於每兩個分組,在一組的先花\(\frac{n}{2}\)次找到哪個最大哪個最小。
然後最大隻要跑每個組中的最大,最小隻要跑每個組的最小就好了。
大概是\(\frac{3}{2}n-2\)次詢問。
int Maxn[N+5],Minn[N+5],A1,A2,x;
void Ramen(int n){
if(n==1){Answer(0,0);return;}RI i;for(i=0;i<n/2;i++) x=Compare(i*2,i*2+1),~x?(Maxn[i]=i*2,Minn[i]=i*2+1):(Maxn[i]=i*2+1,Minn[i]=i*2);if(n&1) Maxn[n/2]=Minn[n/2]=n-1;
A1=Maxn[0];A2=Minn[0];for(i=1;i<(n+1)/2;i++) ~Compare(Maxn[i],A1)&&(A1=Maxn[i]),~Compare(A2,Minn[i])&&(A2=Minn[i]);Answer(A2,A1);
}