11-03 模拟赛Day2

发布时间 2023-11-03 21:44:33作者: 月光幻影

decimal

  • 直接模拟笔算除法即可

  • $ n % m $ 的前 $ l - 1 $ 位的余数可以 $ O(1) $ 求出来,为 $ n \times 10 ^ {l - 1} % m $

    • 这里的‘余数’是将余数乘以 $ 10 ^ {l - 1} $ 后化为的正整数
  • $ R - L \le 10 ^ 5 $ 很特殊,使得可以 $ O(n) $ 模拟笔算除法,商为 $ num \times 10 / m $,余数为 $ num \times 10 % m $

  • 题目与循环节无关,因为循环节可以超级长,赛时 Floyd 判环被卡了

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N = 1e6 + 10;

int n, m, l, r;

int Q_pow(int a, int b, int mod){
	int ans = 1, p = a;
	while(b){
		if(b & 1){
			ans = (ans * p) % mod;
		}
		b >>= 1;
		p = (p * p) % mod;
	}
	return ans;
}

signed main(){
//	freopen("1.in", "r", stdin);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);

	cin >> n >> m >> l >> r;
	int num = (n * Q_pow(10, l - 1, m)) % m;
	for(int i = l; i <= r; i++){
		cout << num * 10 / m;
		num = num * 10 % m;
	}
	cout << "\n";
}
//Zyb Txdy

labor

  • 冒泡排序的次数 = 逆序对个数

  • $ O(n log ^ 2 n) $

    • 不同长度的贡献随长度增加单调不减

    • 二分最短长度 + $ O(n log n) $ 维护区间逆序对

  • $ O(n log n) $

    • 左端点相同的所有区间逆序对个数单调不减(在末尾加上一个元素,逆序对不可能减少)

    • 尺取法,在当前左端点下找到了符合条件的最小右端点后,左端点++,继续

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N = 1e6 + 10;

int n, x;
int a[N];

int t[N];

int lowbit(int x){
	return x & (- x);
}

void Insert(int pos, int val){
	while(pos < N){
		t[pos] += val;
		pos += lowbit(pos);
	}
}

int Query_fr(int pos){
	int ans = 0;
	while(pos > 0){
		ans += t[pos];
		pos -= lowbit(pos);
	}
	return ans;
}

bool Check(int len){
	int ans = 0;
	for(int i = 1; i < N; i++){
		t[i] = 0;
	}
	for(int i = 1; i <= n; i++){
		if(i > len){
			ans -= Query_fr(a[i - len] - 1);
			Insert(a[i - len], -1);
		}
		ans += Query_fr(n) - Query_fr(a[i]);
		Insert(a[i], 1);
		if(ans >= x && i >= len){
			return 1;
		}
	}
	return 0;
}

signed main(){
//	freopen("1.in", "r", stdin);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);

	cin >> n >> x;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
	}
	int l = 1, r = n;
	if(n <= (int)1e3){
		while(l < r){
			int mid = (l + r) / 2;
			if(Check(mid)){
				r = mid;
			}else{
				l = mid + 1;
			}
		}
		cout << r << "\n";
	}else{
		if(Check(80)){
			r = 80;
		}
		while(l < r){
			int mid = (l + r) / 2;
			if(Check(mid)){
				r = mid;
			}else{
				l = mid + 1;
			}
		}
		cout << r << "\n";		
	}
}
//Zyb Txdy

distance

  • 将删去一条边后的联通块加在当前‘最优点’上一定不劣

  • 以点 u 为根的子树除了重心方向的子树外,其他子树的大小一定 <= n / 2

  • 操作一定是将原重心的子树拆下来连到‘最优点’上,使得拆下来的总和 >= n / 2,即‘最优点’重心方向的子树的 si <= n / 2

  • 如果将当前最优点所在的、以重心为根的子树保留(不拆下来),仍能使 max_si <= n / 2,则可以少删一条边

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N = 1e6 + 10;

int n;
int u, v;
int ancs[N], si[N];
int mini, rt, rtt;
vector <int> e[N], vec;

void Dfs(int x, int y){
	if(x == rt) ancs[x] = 0;
	else if(y == rt) ancs[x] = x;
	else ancs[x] = ancs[y];
	
	si[x] = 1;
	int maxi = 0;
	for(auto i : e[x]){
		if(i == y) continue;
		Dfs(i, x);
		si[x] += si[i];
		maxi = max(maxi, si[i]);
	}
	maxi = max(maxi, n - si[x]);
	if(maxi < mini){
		mini = maxi;
		rtt = x;
	}
}

signed main(){
//	freopen("1.in", "r", stdin);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);

	cin >> n;
	for(int i = 1; i < n; i++){
		cin >> u >> v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	rt = 1;
	mini = (int)1e18;
	Dfs(rt, 0);
	
	rt = rtt;
	Dfs(rt, 0);
	for(auto i : e[rt]){
		vec.push_back(si[i]);
	}
	sort(vec.begin(), vec.end(), greater <int> ());
	
	int tot = 0, edge = 0, mid = n / 2 + n % 2;
	for(auto i : vec){
		tot += i;
		edge++;
		if(tot >= mid){
			mini = i;
			break;
		}
	}
	for(int i = 1; i <= n; i++){
		int rtt = ancs[i];
		if(rtt == 0){
			cout << "0\n";
		}else if(tot - (si[rtt] >= mini ? si[rtt] : mini) + si[i] >= mid){
			cout << edge - 1 << "\n";
		}else{
			cout << edge << "\n";
		}
	}
}
//Zyb Txdy

worship

  • 只会 15 pts 的状压……

  • $ dp(i, j) $ 表示已经选了的点组成的集合为 i,上一个点为 j 的和

\[ dp(i | (1 << (k - 1)), k) += dp(i, j) * dep(Lca(i, j)) \]

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int MOD = 1e9 + 7;
const int N = 2e6 + 10;

int n;
int fa[N], dep[N];
int dp[N][25], lca[25][25];
vector <int> e[N];

void Dfs(int x, int y){
	dep[x] = dep[y] + 1;
	for(auto i : e[x]){
		if(i == y){
			continue;
		}
		Dfs(i, x);
	}
}

int Lca(int x, int y){
	while(x != y){
		if(dep[x] < dep[y]){
			swap(x, y);
		}
		x = fa[x];
	}
	return x;
}

signed main(){
//	freopen("1.in", "r", stdin);
	cin >> n;
	for(int i = 2; i <= n; i++){
		cin >> fa[i];
		e[i].push_back(fa[i]);
		e[fa[i]].push_back(i);
	}
	Dfs(1, 0);
	for(int i = 1; i <= n; i++){
		for(int j = 1; j < i; j++){
			lca[i][j] = lca[j][i] = Lca(i, j);
		}
	}
	
	int up = (1 << n) - 1;
	for(int i = 1; i <= n; i++){
		dp[1 << (i - 1)][i] = 1;
	}
	for(int i = 1; i <= up; i++){
		for(int j = 1; j <= n; j++){
			if(!(i >> (j - 1) & 1)){
				continue;
			}
			for(int k = 1; k <= n; k++){
				if((i >> (k - 1) & 1)){
					continue;
				}
				(dp[((1 << (k - 1)) | i)][k] += dp[i][j] * (dep[lca[j][k]]) % MOD) %= MOD;
			}
		}
	}
	int ans = 0;
	for(int i = 1; i <= n; i++){
		(ans += dp[up][i]) %= MOD;
	}
	cout << ans << "\n";
}
//Zyb Txdy