Embiid  

E. Delete a Segment

题意:

给你n个线段进行区间覆盖,删除线段\(seg_i\)后,其余线段覆盖区间有还有\(cnt_i\)段连续的,求\(cnt_1-cnt_n\)中的最大值。

解法:

整数点线段覆盖区间,根据套路,将端点乘上2,乘上2加一,乘上2减一全部推进离散化。
然后对每条线段覆盖区域进行差分。此时,若不删去线段,要求有多少段连续覆盖,就是求有多少段连续区域不为0.
考虑删去一条线段,这条线段有多少连续的值为1的段,删去这条线段后就会多出几个区间。再考虑线段两端本来就是断着的减去即可。

#include <bits/stdc++.h>
#define all(x) (x).begin(),(x).end()
using namespace std;

const int maxn = 1e6;
int has[2 * maxn + 11],sum[2 * maxn + 11],l[2 * maxn + 11],r[2 * maxn + 11];
vector <int> point;

void push(int x) {
	x *= 2;
	point.emplace_back(x - 1);
	point.emplace_back(x + 1);
	point.emplace_back(x);
}

int main(){
	int t;
	scanf("%d" , &t);
	while (t--) {
		int n;
		scanf("%d" , &n);
		point.clear();
		for (int i = 1; i <= n; i++) {
			scanf("%d %d",&l[i],&r[i]);
			push(l[i]);
			push(r[i]);
		}
		sort(all(point));
		point.erase(unique(all(point)) , point.end());
		int m = point.size();
		for (int i = 1; i <= n; i++) {
			int L = lower_bound(all(point) , l[i] * 2) - point.begin() + 1;
			int R = lower_bound(all(point) , r[i] * 2) - point.begin() + 1;
			sum[R + 1]--; sum[L]++;
		} 
		for (int i = 1; i <= m; i++) sum[i] += sum[i - 1];
		for (int i = 1; i <= m; i++)
			if (sum[i - 1] != 1 && sum[i] == 1) has[i] = has[i - 1] + 1; else has[i] = has[i - 1];
		int ans = 0;
		bool flag = false;
		for (int i = 1; i <= m; i++)
			if (sum[i] == 0) { if (flag) ans++; flag = false; } else flag = true;
		int mx = 0;
		for (int i = 1; i <= n; i++) {
			int L = lower_bound(all(point) , l[i] * 2) - point.begin() + 1;
			int R = lower_bound(all(point) , r[i] * 2) - point.begin() + 1;
			int cnt = has[R] - has[L - 1];
			if (sum[L] == 1 && sum[L - 1] == 1) cnt++;
			if (sum[R] == 1 && sum[R + 1] == 0) cnt--;
			if (sum[L] == 1 && sum[L - 1] == 0) cnt--;
			
			mx = max(mx , ans + cnt);
		}
		printf("%d\n" , mx);
		for (int i = 1; i <= m; i++) sum[i] = 0,has[i] = 0;
	} 
} 

F. Classical?

题意:

解法:

\(lcm(i,j)=\frac {i*j}{gcd(i,j)}=\frac {i}{gcd(i,j)}*\frac{j}{gcd(i,j)}*gcd(i,j)\)。所以我们考虑枚举gcd,假设为g。对于每个g,要使lcm最大,就是要使剩下来的互质的两个数最大。对于每个g,我们考虑从大到小枚举倍数,并且用一个栈来模拟这个过程,栈里始终只保存还可能出现更优解的倍数。如果已经枚举过的某个倍数j与现在枚举到的倍数i互质的话,一个有效答案gij就出现了,此时,i后面的数不管是否与i,j互质,与i或j产生的答案都不会更大了,所以i,j在之后枚举中就不用考虑了,并且所有小于j的数都不会产生更优的解,我们也可以将他们弹出栈。现在我们要考虑的就是找到什么时候为止了。我们只需要知道栈中有多少个与当前倍数i互质的数就行了。对于栈内的数动态维护,利用莫比乌斯函数容斥一下就可以得到了。
比如说,我们考虑与30互质的数的个数,我们减去栈中2的倍数,减去栈中3的倍数,减去栈中5的倍数,在加上6的倍数,10的倍数,15的倍数,减去30的倍数。加或者减其实就是莫比乌斯函数,所以我们维护倍数,莫比乌斯函数作为加减号的确定依据,容斥得到答案就可以了。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5;
int cnt = 0;
int prime[maxn + 11];
bool vis[maxn + 11] = {false};
int res[maxn + 11],mu[maxn + 11];
vector <int> d[maxn + 11];

void pre() {
	for (int i = 1; i <= maxn; i++)
		for (int j = i; j <= maxn; j += i)
			d[j].emplace_back(i);
	mu[1] = 1;
	for (int i = 2; i <= maxn; i++) {
		if (!vis[i]) { prime[++cnt] = i; mu[i] = -1; }
		for (int j = 1; j <= cnt; j++) {
			if (i * prime[j] > maxn) break;
			vis[i * prime[j]] = true;
			if (i % prime[j] == 0) {
				mu[i * prime[j]] = 0;
				break;
			}
			mu[i * prime[j]] = -mu[i];
		}
	}
	for (int i = 1; i <= maxn; i++) vis[i] = false;
}

int gcd(int a,int b) { return b == 0 ? a : gcd(b , a % b); }

void upd(int x,int val) { for (auto i : d[x]) res[i] += val; }

int calc_prime(int x) {
	int ans = 0;
	for (int i : d[x]) ans += res[i] * mu[i];
	return ans;
}

int main(){
	pre();
	int n;
	scanf("%d" , &n);
	long long ans = 0;
	for (int i = 1; i <= n; i++) {
		int x;
		scanf("%d" , &x);
		ans = max(ans , 1ll * x);
		vis[x] = true;
	} 
	for (int g = 1; g <= maxn; g++) {
		stack <int> s;
		for (int i = maxn / g; i >= 1; i--) {
			if (!vis[i * g]) continue;
			int num = calc_prime(i);
			bool in = num == 0 ? true : false;
			while (num) {
				if (gcd(s.top() , i) == 1) {
					ans = max(ans , 1ll * i * s.top() * g);
					num--;
				}
				upd(s.top() , -1);
				s.pop();
			}
			if (in) {  upd(i , 1); s.push(i); }
		}
		while (!s.empty()) {
			upd(s.top() , -1);
			s.pop();
		}
	} 
	printf("%lld\n" , ans);
} 

posted on 2020-02-08 21:46  Embiid  阅读(115)  评论(0编辑  收藏  举报