[csp2019-jx] 散步
有乙個長度為 \(l\) 的環 \((2 \le l \le 10^9)\),
環上有 \(n\) 個人, \(m\) 個出口 \((1 \le n,m \le 2 \times 10^5)\).
規定第乙個出口的位置為 \(0\), 並將 "到第乙個出口的距離" 定義為 "從順時針方向走到第乙個路口所走的路程".
每個人有兩個屬性 \(t_i,\ x_i\), 分別表示這個人的前進方向 (\(0\) 為逆時針, \(1\) 為順時針), 以及這個人到第乙個出口的距離.
每個出口有兩個屬性 \(lim_i,\ a_i\), 分別表示這個出口的人數限制 (即最多能從這個出口出去多少人), 以及這個出口到第乙個出口的距離.
在每個單位時間內, 所有人都會朝著他們的前進方向移動乙個單位距離,
若有乙個人 \(i\) 走到了乙個人數沒有達到上限的出口 \(j\), 他就會從這個出口出去, 則 \(k[i]=j\).
特別地, 若有兩個人同時到達了乙個只能出去乙個人的出口, 則編號較小的那個人可以出去.
最終輸出 \(i \times k[i]\) 的異或和, 即
\[(1 \times k[1]) \oplus (2 \times k[2]) \oplus \dots \oplus (n \times k[n])
\](注: \(\oplus\) 為異或符號)
首先, 最暴力的思路就是大模擬, 每次把每乙個人往他的移動方向移一格, 然後從小到大掃一遍, 看哪個人可以出去.
可以發現, 我們其實只需要找到離他下乙個出口最近的那個人, 記他離出口的距離為 \(mind\), 把所有人往前移動 \(mind\) 個單位距離就行了.
當然, 有可能出現這個人到了出口後, 發現出口已經被填滿了, 那麼我們就需要再去找他的下乙個出口, 而這是乙個最劣能達到 \(o(n)\) 的操作, 這樣的話複雜度就有可能退化到 \(o(n^3)\).
所以, 我們需要用(雙向的)並查集優化一下這個過程, 總複雜度為 \(o(\alpha n^3)\).
但還要注意的是, 不是每一次都能使得至少有乙個人出去, 因為當前離出口最近的那個人他指向的出口已經被填滿了, 所以複雜度應該是 \(o(玄學 \times \alpha n^3)\)
**在下面.
稍微思考一下, 我們可以發現, 並不需要維護每乙個人的位置. 因為乙個人出去後, 其他人到達每乙個出口的相對時間並沒有改變, 每次我們只需要取到下乙個出口最近的人, 然後把它移到出口即可.
如果這個出口沒有滿, 則不需要進行操作, 下一次直接查詢最小值即可.
如果這個出口滿了, 那麼我們需要修改原來要從這個出口出去的人離它下乙個出口的位置,
可以把這些人分為 在這個出口左邊的 和 在這個出口右邊的.
對於左邊的人, 把他們的目標轉移到該出口右邊第乙個沒有滿的出口,
對於右邊的人, 把他們的目標轉移到該出口左邊第乙個沒有滿的出口,
至於查詢兩邊 第乙個沒有滿的出口, 可以用並查集解決.
分析一下複雜度: 每次都會讓乙個人從出口出去, 所以最多迴圈 \(n\) 次, 每次要查詢最小值和區間修改, 還要在並查集中查詢下乙個出口,
所以總複雜度為 \(o(n\log n \alpha)\)
#includeusing namespace std;
const int n=1e3+7;
const int inf=0x3f3f3f3f;
int n,m,l,d[n],nxt[n],le[n],ri[n],ans,a[n],x[n],minx=inf,nm,nn,lim[n];
bool t[n],gon[n];
int exi[n];
int dis(int i,int j)
le[1]=ri[1]=1;
for(int i=1;i<=m;i++) scanf("%d",&lim[i]);
for(int i=1;i<=n;i++)
else
if(!nxt[i]) nxt[i]=m;
if(nxt[i]>m) nxt[i]=1;
tmp=nxt[i];
}} else
}if(!gon[i]) minx=min(minx,dis(i,nxt[i]));
}} printf("%d\n",ans);
//for(int i=1;i<=n;i++) printf("%d ",exi[i]); putchar('\n');
return 0;
}
#includeusing namespace std;
const int n=2e5+7;
const int inf=0x3f3f3f3f;
int n,m,l;
int a[n],lim[n],t[n],x[n],id[n],ll[n],lr[n],rl[n],rr[n],le[n],ri[n],minx[4*n],tag[4*n],divd,d[n],nxt[4*n],dye[4*n],num[4*n],bel[n];
int seq[n],pac[n][2],nn,nm;
long long ans;
bool rule(int i,int j)
int fl(int x)
int fr(int x)
void read()
for(int i=1;i<=n;i++)
sort(seq+1,seq+1+n,rule);
for(int i=1;i<=n;i++)
}void pre()
ll[1]=t1+1; t1=1;
if(ll[1]>divd) ll[1]=0;
while(t2<=m)
lr[t2]=t1-1;
if(t2!=1) ll[t2]=lr[t2-1]+1;
t2++;
} if(!lr[1]) lr[1]= ll[1] ?divd :0 ;
else if(!ll[1]) ll[1]=1;
t1=n; t2=m;
rr[m]=n;
while(t2)
rl[t2]=t1+1;
if(t2!=m) rr[t2]=rl[t2+1]-1;
t2--;
} for(int i=1;i<=m;i++)
}void rfs(int k)
else if(tmp>0)
else
}void build(int k,int l,int r)
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
rfs(k);
}void upd(int k,int w,int v)
void psd(int k)
int query(int k,int l,int r,int x,int y,int id)
void modify(int k,int l,int r,int x,int y,int w,int v)
psd(k);
int mid=(l+r)>>1;
if(x<=mid) modify(k<<1,l,mid,x,y,w,v);
if(y>mid) modify(k<<1|1,mid+1,r,x,y,w,v);
rfs(k);
}void recon(int k)while(!lim[t1]);
if(!ll[t1])
else ll[t1]=ll[k];
if(ll[k]<=lr[k]) modify(1,1,n,ll[k],lr[k],dis(k,t1,0),t1);
else
} if(rl[k])while(!lim[t1]);
if(!rl[t1])
else rr[t1]=rr[k];
if(rl[k]<=rr[k]) modify(1,1,n,rl[k],rr[k],dis(k,t1,1),t1);
else
}}void find(int k,int l,int r)
return;
} psd(k);
int mid=(l+r)>>1,t1=minx[k<<1]-minx[k<<1|1],t2=num[k<<1]-num[k<<1|1];
if(t1<0||(t1==0&&t2<0)) find(k<<1,l,mid);
else if(t1>0||(t1==0&&t2>0)) find(k<<1|1,mid+1,r);
}int main()
CSP2019 劃分 解題報告
csp2019 劃分 有乙個長度為 n 的序列 a i n le 4 times 10 7 求 sum a i 2 sum a i 2 dots sum 1 a i 2 的最小值.結論 上述式子能取到最小值,當且僅當最後一塊 即 sum 1 a i 取到了最小值.證明 暫時不會.那我們就 dp 保證...
CSP2019 樹的重心 解題報告
csp2019 樹的重心 t 組資料 1 le t le 5 每次給定一棵 n 個點的樹 1 le n le 299995 設 e 為樹的邊集,v x,v y 分別為刪去邊 x,y 後 點 x 所在的點集和點 y 所在的點集.求 sum left sum x 是 v x 的重心 x sum y 是 ...
CSP2019 劃分 解題報告
csp2019 劃分 有 n 個非負整數 a i n le 4 10 7 將它們分為若干部分,記為 s i 要求 s ge s i 設 res sum s i 2 求 res 的最小值 考場上寫的做法.設 f i j 為第一段的起點為 i 終點為 j 時 res 的最小值.樸素做法直接列舉 i,j,...