2021牛客暑期多校训练营5
B: Boxes
- 题意
给你n个盒子,每个盒子只有黑白两种情况,而且支付C元就可以知道未开过的盒子中黑盒的个数,问你最少知道每个盒子中球颜色的期望
- 思路
C(n,i) / (2 ^ n) 拿i个的概率
- 不问的情况 (sum w_i)
- (w_i)升序排序,C+ (sum w_i(1 - 1/(2 ^ {n - i}))) 求后面有i个全是同色的情况
实在不行可以这样理解, 首先 2 / 16是不用算的, 那么 7 / 8的都要算第一个,并在之后判断是否可行, 那么1个和n-1个盒子不一样的情况,只有2中之后 16 - 2 - 2 / 16 = 3 / 4同上, 之后每种情况2中,那么 (w_1)的贡献就是 7 / 8,(w_2)的贡献是 3 / 4以此类推即可
本题是队友过的,我太菜,呜呜呜
code :
double w[N];
void solve(){
int n;
db C;
cin >> n >> C;
db sum = 0;
fep(i,1,n) cin >> w[i], sum += w[i];
db ans = C;
sort(w + 1,w + 1 + n);
for(int i = 1;i <= n;i ++) {
ans += w[i] * (1 - 1.0 / (pow(2.0, n - i)));
}
ans = min(ans, sum);
printf("%.6f
", ans);
}
D: Double Strings
- 题意
好的方案的构成是“一段相同的前缀+一个不同字符(a比b小)+长度相同的任意后缀”,求一共多少个好的方案
- 思路
dp求最长公共子序列,组合数学求后面的即可,其实读懂了题就是简单(
sb)题
code :
typedef long long LL;
const int N = 5010, MOD = 1e9+7;
ll fact[N * 2], infact[N * 2];
void init() {
fact[0] = infact[0] = 1;
for (int i = 1 ; i < N * 2; ++i) {
fact[i] = fact[i - 1] * i % MOD;
infact[i] = infact[i - 1] * pow_mod(i, MOD - 2) % MOD;
}
}
ll C(ll a,ll b) {
if(a<0||b<0||a<b) return 0;
return 1LL * fact[a] * infact[a - b] % MOD * infact[b] % MOD;
}
char a[N], b[N];
ll f[N][N];
void solve(){
cin >> (a + 1) >> (b + 1);
ll lena = strlen(a + 1), lenb = strlen(b + 1);
// 最长公共子序列
for(int i = 1;i <= lena;i ++) {
for(int j = 1;j <= lenb;j ++) {
if(a[i] == b[j]) f[i][j] = (f[i - 1][j] + f[i][j - 1] + 1) % mod;
else f[i][j] = (f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1]) % mod;
}
}
ll ans = 0;
for(int i = 1;i <= lena;i ++) {
for(int j = 1;j <= lenb;j ++) {
if(a[i] < b[j]) {
// 最长前缀+当前+任意后缀
ans = (ans + 1LL * (f[i - 1][j - 1] + 1) % mod * C(lena - i + lenb - j, min(lena - i, lenb - j)) % mod + mod) % mod;
}
}
}
cout << ans << endl;
}
H: Holding Two
- 题意
构造一个n行m列的数组,每个数字由0和1组成,每行|竖|斜着连着三个数字是一样的都不行,让你构造
- 思路
我的构造方法 :
01010101010101
01010101010101
10101010101010
10101010101010
01010101010101
这样交替下去
code :
int main() {
int n,m;
cin >> n >> m;
int cnt = 0;
for(int i = 1;i <= n;i ++) {
cnt ++;
for(int j = 1;j <= m;j ++) {
if(cnt != 3 && cnt != 4) {
if(j & 1) {
cout << "0";
}else {
cout << "1";
}
}else {
if(j & 1) {
cout << "1";
}else {
cout << "0";
}
}
}
if(cnt == 4) cnt = 0;
cout << endl;
}
}
J: Jewels
- 题意
有n个宝石,三维平面,你位于(0,0,0), 抓一个宝石的消耗为此宝石当前与你的距离的平方(x * x + y * y + z * z ), 你每秒只能抓一个宝石,其他宝石会以(v_i)的速度往下掉
- 思路
费用流建图,二分图,左边时刻,右边宝石,跑一遍ek,即可,
牛客这题卡acwing的费用流模板,调死我了,其实多加个优先队列去优化即可
code :
const int N = 610, M = N * N;
const int INF = 1e9;
int n,m,S,T;
int h[N],e[M],f[M],ne[M],idx;
int incf[N],pre[N];
bool st[N];
ll d[N],w[M],ans = 0;
struct node {
ll x,y,z,v;
}a[N];
inline void add(int a,int b,int c,ll d){
e[idx] = b,ne[idx] = h[a],f[idx] = c,w[idx] = d,h[a] = idx ++;
e[idx] = a,ne[idx] = h[b],f[idx] = 0,w[idx] = -d,h[b] = idx ++;
}
inline bool spfa(){
for(int i= 0;i < 2 * n + 3;i ++ ) {
d[i] = 2e18;
incf[i] = 0;
}
priority_queue<pair<ll,int> >q;
q.push(make_pair(0,S));
d[S] = 0,incf[S] = INF;
while(!q.empty()){
int t = q.top().second;
q.pop();
st[t] = false;
for(int i = h[t];~i;i = ne[i]){
int j = e[i];
if(f[i] && d[j] > d[t] + w[i]){
d[j] = d[t] + w[i];
pre[j] = i;
incf[j] = min(f[i],incf[t]);
if(!st[j]){
st[j] = 1;
q.push(make_pair(-1LL * d[j],j));
}
}
}
}
return incf[T] > 0;
}
ll EK(){
ll cost = 0;
while(spfa()){
ll t = incf[T];
cost += t * d[T];
for(int i = T;i != S;i = e[pre[i] ^ 1]){
f[pre[i]] -= t;
f[pre[i] ^ 1] += t;
}
}
ans = ans + cost;
return cost;
}
void solve(){
scanf("%d", &n);
for(int i = 1;i <= n;i ++) {
scanf("%lld%lld%lld%lld", &a[i].x, &a[i].y, &a[i].z, &a[i].v);
ans += 1LL * a[i].x * a[i].x + a[i].y * a[i].y;
}
memset(h,-1,sizeof h);
S = 2 * n + 2,T = 2 * n + 1;
for(int i = 0;i < n;i ++) {
add(S,i,1,0);
}
for(int i = n + 1;i <= 2 * n;i ++) {
add(i,T,1,0);
}
for(int i = 0;i < n;i ++) {
for(int j = n + 1;j <= 2 * n;j ++) {
add(i,j,1,(a[j - n].z + i * a[j - n].v) * (a[j - n].z + i * a[j - n].v));
}
}
EK();
printf("%lld
", ans);
}
K: King of Range
- 题意
给你一个长度为n的序列,让你找到有多少对(l,r) 另 (max(a_l,a_{l+1},···,a_r)) - (min(a_l,a_{l+1},···,a_r))严格大于k,并且给出m次k
- 思路
单调队列或单调栈或双端队列(
队友写过的) 去维护当前最大值最小值的下标,并且维护不能取的数量,每次用n * (n + 1) / 2减去即可
code :
#define int long long
int n,m;
int a[N];
int qa[N],qi[N];
void solve(){
cin >> n >> m;
fep(i,1,n) cin >> a[i];
while(m --) {
int ans = (n + 1) * n / 2;
int k;
int h1 = 0,t1 = -1, h2 = 0, t2 = -1;
cin >> k;
int l = 1;
for(int i = 1;i <= n;i ++) {
// 记录最大值
while(h1 <= t1 && a[qa[t1]] < a[i]) t1 --;
qa[++ t1] = i;
// 记录最小值
while(h2 <= t2 && a[qi[t2]] > a[i]) t2 --;
qi[++ t2] = i;
while(h1 <= t1 && h2 <= t2 && a[qa[h1]] - a[qi[h2]] > k) {
// 删除贡献
if(qa[h1] == l) h1 ++;
if(qi[h2] == l) h2 ++;
l ++;
}
ans -= (i - l + 1);
}
cout << ans << endl;
}
}