F. Summoning Minions
贪心:每次一定取 (k-1) 个不删,然后每次取一个删一个,最后取 (1) 个保留。
对于每个点分别算贡献,即为对其他的贡献的 (b_i) 和(如果自己保留)贡献的 (a_i)
假设所有保留的为集合 (A),则 (A) 中的元素一定是按照 (b_i) 递增顺序加入。
因此按照 (b_i) 排序,进行 DP 即可。(mathcal O(n^2))
当然可以对每个东西和每个位置跑带权匹配,但我被卡了
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
#define File(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
template<class T> void upmax(T &x, T y){x = x>y ? x : y;}
template<class T> void upmin(T &x, T y){x = x<y ? x : y;}
struct item{
int a, b, id;
};
item a[80];
int f[80][80];
bool from[80][80];
bool isA[80];
int A[80];
int main(){
// File("cf1354f");
int T;
scanf("%d", &T);
while(T--){
int k, n;
scanf("%d%d", &n, &k);
for(int i=1; i<=n; i++)
scanf("%d%d", &a[i].a, &a[i].b), a[i].id = i;
sort(a + 1, a + 1 + n, [](item p, item q) {return p.b < q.b;});
memset(f, 0x9f, sizeof(f));
f[0][0] = 0;
for(int i=1; i<=n; i++){
f[i][0] = f[i-1][0] + a[i].b * (k - 1);
for(int j=max(1, k - (n - i)), li=min(i, k); j<=li; j++){
int tA = a[i].a + a[i].b * (j - 1) + f[i-1][j-1];
int tB = a[i].b * (k - 1) + f[i-1][j];
if(tA > tB)
f[i][j] = tA, from[i][j] = true;
else
f[i][j] = tB, from[i][j] = false;
}
}
for(int p=n, q=k; p!=0; --p){
isA[p] = from[p][q];
if(isA[p]) A[q] = a[p].id;
q -= from[p][q];
}
printf("%d
", n * 2 - k);
for(int i=1; i<k; i++) printf("%d ", A[i]);
for(int i=1; i<=n; i++)
if(!isA[i]) printf("%d -%d ", a[i].id, a[i].id);
printf("%d
", A[k]);
}
return 0;
}
G. Find a Gift
因为不知道哪个是重物,很难处理,必须先找到一个重物作为切入点。
随机若干个物品和 (1) 称重,如果 (1) 较轻,则答案就是 (1)。
否则认为 (1) 是重物,依次倍增,判断 ([1,2^k]) 是否都是重物,找到第一个不是重物的区间,可以和前面全是重物的区间进行比较,二分即可。
几个部分询问次数都是 (log) 级的。
#include <iostream>
#include <random>
using namespace std;
int ask(int l1, int r1, int l2, int r2){
cout << "? " << r1 - l1 + 1 << ' ' << r2 - l2 + 1 << ' ' << '
';
for(int i=l1; i<=r1; i++)
cout << i << ' ';
cout << '
';
for(int i=l2; i<=r2; i++)
cout << i << ' ';
cout << endl;
char res[10];
cin >> res;
if(res[0] == 'E') return 0;
if(res[0] == 'F') return 1;
if(res[0] == 'S') return 2;
return -1;
}
int main(){
cin.sync_with_stdio(false);
cout.sync_with_stdio(false);
int T;
cin >> T;
mt19937 rnd((unsigned long long)&T);
while(T--){
int n, k;
cin >> n >> k;
uniform_int_distribution<int> D(2, n);
bool ok = false;
for(int i=1; i<=25; i++){
int pos = D(rnd);
int r = ask(1, 1, pos, pos);
if(r == 2){
cout << "! " << 1 << endl;
ok = true;
break;
}
}
if(ok) continue;
int L, R;
for(int i=1; i*2<=n; i<<=1){
int r = ask(1, i, i + 1, 2 * i);
if(r == 1){
L = i + 1, R = 2 * i;
break;
}
L = i * 2 + 1, R = n;
}
int res = R;
while(L <= R){
int mid = (L + R) >> 1;
if(ask(1, mid - L + 1, L, mid) == 1)
res = mid, R = mid - 1;
else L = mid + 1;
}
cout << "! " << res << endl;
}
return 0;
}