A层邀请赛5

A层邀请赛5

A 赤

我不会\(wqs\)二分,但是这个题他是\(wqs\)二分套\(wqs\)二分。。。。

首先不难搞到一个\(n^3\)\(DP\)

定义\(f[i][j][k]\)表示前\(i\)只猫,用了\(j\)个第一种零食,\(k\)个第二种零食的捕获的猫的期望,转移考虑当前猫用哪些零食

如何优化这个\(dp\)

我们想能不能砍掉一维,\(i\)显然砍不掉,\(j\),\(k\)形式相同

我们尝试砍掉\(k\)

砍掉之后,直接\(dp\)显然会超出限制,用什么方法能够让选择更多第二种零食不是越多越优秀呢?

可以发现,如果每选一个\(b\)都给期望减去一个值,那么选第二种零食就不是越多越好

这令我们想到\(tree\)那个题,那么这个题是否可以\(wqs\)二分?

以横轴为选择第二种零食的数量,纵轴为期望,由于我们每次选取最优策略,所以斜率单调不增,这是一个上凸包!可以\(wqs\)二分

二分\(dt_b\)\(dp\)时每选择一个第二种零食就减去一个\(dt_b\),同步处理使用第二种零食的数量,最后撤掉\(dt_b\)的贡献,根据数量调整二分区间,(其实是改变斜率,不停的切凸包)

于是我们可以砍掉一维\(k\),复杂度变成\(n^2logn\)但是还是过不了

继续优化,我们上面提到\(j\),\(k\)形式相同,那么\(k\)能砍掉,\(j\)为什么不能?

砍掉\(j\)以后,以同样的方式处理,每一次\(dt_a\)的二分都重新二分\(dt_b\)

这样我们就有了\(wqs\)二分套\(wqs\)二分的\(n log ^ 2n\)的优秀做法

code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100003;
int n, a, b;
double pa[maxn], pb[maxn], pab[maxn];
double f[100005], ca[100005], cb[100005];
typedef pair<int, int> pii;
pii solve(double mid, double mid2){
	for(int i = 1; i <= n; ++i){
		f[i] = f[i - 1];
		ca[i] = ca[i - 1];
		cb[i] = cb[i - 1];
		if(f[i] < f[i - 1] + pa[i] - mid){
			f[i] = f[i - 1] + pa[i] - mid;
			ca[i] = ca[i - 1] + 1;
			cb[i] = cb[i - 1];
		}
		if(f[i] < f[i - 1] + pb[i] - mid2){
			f[i] = f[i - 1] + pb[i] - mid2;
			ca[i] = ca[i - 1];
			cb[i] = cb[i - 1] + 1;
		}
		if(f[i] < f[i - 1] + pab[i] - mid - mid2){
			f[i] = f[i - 1] + pab[i] - mid - mid2;
			ca[i] = ca[i - 1] + 1;
			cb[i] = cb[i - 1] + 1;
		}
	}
	return pii(ca[n],cb[n]);
}
int main(){
	while(~scanf("%d%d%d",&n, &a, &b)){
		for(int i = 1; i <= n; ++i)scanf("%lf",&pa[i]);
		for(int i = 1; i <= n; ++i)scanf("%lf",&pb[i]);
		for(int i = 1; i <= n; ++i)pab[i] = pa[i] + pb[i] - pa[i] * pb[i];
		double l = 0, r = 1, mid, ll = 0, rr = 1, midd;
		for(int i = 1; i <= 50; ++i){
			mid = (l + r) / 2;
			ll = 0, rr = 1;	
			for(int i = 1; i <= 50; ++i){
				midd = (ll + rr) / 2;
				if(solve(mid, midd).second > b)ll = midd;
				else rr = midd;
			}
			if(solve(mid, rr).first > a)l = mid;
			else r = mid;
		}
		solve(r, rr);
		printf("%lf\n",f[n] + r * a + rr * b);
	}
	return 0;
}

B Tourist Attractions

首先暴力\(n^3\)或者\(n^4\)枚举有\(70\)

正解也是枚举,但是有\(bitset\)优化

枚举中间两个点,计算贡献,减去非法贡献

非法贡献是什么,显然是构成了三元环,那么我们把他俩的\(bitset\)与一下,减去\(count\)即可

\(STL\)大法好,可惜我不会

code
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<bitset>
using namespace std;
const int maxn = 1505;
int n;
long long ans = 0;
bitset <maxn> b[maxn];
char c[maxn];
int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i){
		scanf("%s",c + 1);
		for(int j = 1; j <= n; ++j)
		  if(c[j] == '1')b[i][j] = 1;
	}
	for(int i = 1; i <= n; ++i){
		for(int j = 1; j <= n; ++j){
			if(b[i][j] == 0)continue;
			int r1 = b[i].count() - 1;
			int r2 = b[j].count() - 1;
			int r3 = (b[i]&b[j]).count();
			ans += r1 * r2 - r3;
		}
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2022-08-02 10:13  Chen_jr  阅读(29)  评论(0编辑  收藏  举报