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;
}