• Codeforces Round #613 (Div. 2)


    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
    " , 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
    " , ans);
    } 
    
    
  • 相关阅读:
    SQLite Helper
    VS2015
    SQLite connection strings
    DELPHI中四种EXCEL访问技术实现
    jQuery
    JQuery笔记
    一张图明白jenkins和docker作用
    多线程实现的4中方法
    线程池ThreadPoolExecutor分析
    Spring Boot配置文件放在jar外部
  • 原文地址:https://www.cnblogs.com/Embiid/p/12285287.html
Copyright © 2020-2023  润新知