2022-02-06 集训题解

排队

link

Desciption

2022-02-06 集训题解

\(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

太水了,懒得说了

烽火戏诸侯

link

Description

2022-02-06 集训题解

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;
}
上一篇:JUC并发编程快速入门篇(四)—— 多线程锁


下一篇:linux_监控zabbix邮件报警