2022-02-06 集训题解
排队
Desciption
\(n,m\le 10^5\)
Solution
不难注意到的是,我们假设 \(f_i\) 为 \(i\) 之前 \(\le a_i\) 的值的个数,那么我们需要满足:
\[\sum_{i=1}^{n} i-f_i=\sum_{i=1}^{n} i-\min(i,a_i)
\]
又因为我们可以知道 \(f_i\le \min(i,a_i)\),所以我们对于每一个 \(i\) 都有 \(f_i=\min(i,a_i)\)。
考虑如何构造这样的 \(a\) 。可以发现,我们选了一个 \(a_i\) 就将 \(a_i\) 这个位置赋值为 \(1\)。那么,\(f_i=i\) 就是选一个比最大值大的值,\(f_i=a_i\) 就是选第一个空位。
考虑到我们可以用 \((i,v)\) 来表示第 \(i\) 步最大值为 \(v\),那么找第一个空位就是向右走一步,找最大值就是向上走若干步。所以答案就是从 \((0,n-\text{maxv})\) 出发到 \((n-m,n-m)\),且不能低于 \(y=x\) 这条直线的方案数,直接用卡塔兰数的方法来就好了。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define mod 1000000007
#define MAXN 200005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
int n,m,a[MAXN],fac[MAXN],ifac[MAXN];
int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
int qkpow (int a,int b){
int res = 1;for (;b;b >>= 1,a = mul (a,a)) if (b & 1) res = mul (res,a);
return res;
}
void Add (int &a,int b){a = add (a,b);}
void Sub (int &a,int b){a = dec (a,b);}
struct Bit_Tree{
int sum[MAXN];
int lowbit (int x){return x & (-x);}
void modify (int x,int v){for (Int i = x;i <= n;i += lowbit (i)) sum[i] += v;}
int query (int x){int res = 0;for (Int i = x;i;i -= lowbit (i)) res += sum[i];return res;}
}tree;
int binom (int a,int b){
return a >= b ? mul (fac[a],mul (ifac[b],ifac[a - b])) : 0;
}
signed main(){
freopen ("queue.in","r",stdin);
freopen ("queue.out","w",stdout);
read (n,m);
for (Int i = 1;i <= m;++ i) read (a[i]);
int up = 2 * n;
fac[0] = 1;for (Int i = 1;i <= up;++ i) fac[i] = mul (fac[i - 1],i);
ifac[up] = qkpow (fac[up],mod - 2);for (Int i = up;i;-- i) ifac[i - 1] = mul (ifac[i],i);
int maxv = 0,tot;
for (Int i = 1;i <= m;++ i) chkmax (maxv,a[i]);
for (Int i = 1;i <= m;++ i){
tree.modify (a[i],1);
if (tree.query (a[i]) != min (i,a[i])) return puts ("0") & 0;
}
tot = n - maxv;int all = n - m;
write (dec (binom (all + tot,all),binom (all + tot,all + 1)));
return 0;
}
论文查重
Solution
太水了,懒得说了
烽火戏诸侯
Description
Solution
有一个奇怪的贪心,就是说我们一开始先对任意相邻两项都移动到中心,直到不能移动为止。然后我们就对最大最小值进行操作,可以让他们两个向中间动一个,但是中间的节点并不改变。
优化的话,可以发现第一步我们可以使用增量法,那么只需要维护每个斜率为 \(1\) 的段的起点即可,第二步直接左右向中间移动即可。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define int long long
#define MAXN 1000005
#define ll long long
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
int n,top,a[MAXN],sta[MAXN],tot[MAXN];
struct Bit_Tree{
int sum[MAXN];
int lowbit (int x){return x & (-x);}
void modify (int x,int v){for (Int i = x;i <= 1e6;i += lowbit (i)) sum[i] += v;}
int query (int x){int res = 0;for (Int i = x;i;i -= lowbit (i)) res += sum[i];return res;}
void change (int l,int r){modify (l,1),modify (r + 1,-1);}
}t;
signed main(){
freopen ("balefire.in","r",stdin);
freopen ("balefire.out","w",stdout);
read (n);
for (Int i = 1;i <= n;++ i) read (a[i]);
sort (a + 1,a + n + 1),sta[top = 1] = 1;
ll ans = 0;
for (Int i = 2;i <= n;++ i){
int pos = sta[top],lstv = a[i - 1] + t.query (i - 1);
while (pos != 1 && a[i] - (i - pos) >= lstv + 1) a[i] -= i - pos,t.modify (pos,1),lstv ++,ans += 1ll * (i - pos) * (i - pos + 1) / 2,pos = sta[-- top];
if (top == 1){
int tim = (a[i] - lstv) / i;
lstv += tim,a[i] -= tim * (i - 1),t.modify (1,tim),ans += tim * 1ll * (i - 1) * i / 2;
}
if (a[i] != lstv){
if (a[i] > lstv + 1){
int d = a[i] - lstv - 1;
t.change (pos,pos + d - 1),a[i] -= d,ans += 1ll * (i - pos) * (i - pos + 1) / 2 - 1ll * (i - pos - d) * (i - pos - d + 1) / 2;
if (pos != 1) -- top;
sta[++ top] = pos + d;
}
}
else sta[++ top] = i;
a[i] -= t.query (i);
}
for (Int i = 1;i <= n;++ i) a[i] += t.query (i),tot[a[i]] ++;
int miv = a[1],mxv = a[n];
while (miv + 1 < mxv){
int tim = mxv - miv - 1;
if (tot[miv] < tot[mxv]) ans += tim * tot[miv],tot[mxv] -= tot[miv],tot[mxv - 1] += tot[miv],tot[miv + 1] += tot[miv],tot[miv] = 0,miv ++;
else ans += tim * tot[mxv],tot[miv] -= tot[mxv],tot[miv + 1] += tot[mxv],tot[mxv - 1] += tot[mxv],tot[mxv] = 0,mxv --;
}
write (ans),putchar ('\n');
return 0;
}