2021牛客暑期多校訓練營6
F.Hamburger Steak
- 題意
給n個漢堡,m個鍋,每個鍋只能同時做一個漢堡,做第i個漢堡需要\(a_i\)的時間,並且每個漢堡最多只能被兩個鍋做,問在盡可能減少平均每口鍋做漢堡的時間的條件下,每個漢堡的方案。
- 思路
一看,一想,wtf,好難啊,想了半天,在看,emmm,wc,
簽到題
每個漢堡無論被幾個鍋做,都是\(a_i\)那么最大的時間就是\(max(a_i , sum / m + (sum % m != 0))\)
然后直接寫就行了。。
code:
int a[N];
void solve(){
int n,m;
sc("%lld%lld", &n, &m);
int sum = 0, maxn = 0;
for(int i = 1;i <= n;i ++) {
scanf("%lld", &a[i]);
sum += a[i];
maxn = max(maxn,a[i]);
}
int sz = max(maxn, sum / m + (sum % m != 0));
int now = sz;
int cnt = 1;
for(int i = 1;i <= n;i ++) {
if(now >= a[i]) {
pr("1 %d %lld %lld\n", cnt, sz - now, sz - now + a[i]);
now -= a[i];
if(!now) now = sz,cnt ++;
}else {
pr("2 %d %lld %lld %d %lld %lld\n", cnt + 1, 0, a[i] - now, cnt, sz - now, sz);
now = sz - (a[i] - now);
cnt ++;
// if(!now) now += sz, cnt ++;
}
}
}
H.Hopping Rabbit
- 題意
一個二維坐標軸,兔子每次跳的距離是d,並且只向上下左右跳,你需要找到兔子的起始坐標,讓他怎么跳都跳不到給出的陷阱中,陷阱為一個矩陣(\(x_i,y_i \to x_j,y_j\))(左下角到右上角)。
- 思路
掃描線,把空間上所有大小為d * d的矩形合並起來,然后掃一遍,看看有沒有空的就行
code :
struct node {
int l,r;
};
// sum維護區間最小值
int sum[N << 2], laz[N << 2];
void update(int u,int l,int r,int L,int R,int v) {
if(L > r || l > R) return;
if(L <= l && R >= r) {
sum[u] += v;
laz[u] += v;
return;
}
int mid = l + r >> 1;
if(L <= mid) update(u << 1, l, mid, L, R, v);
if(R > mid) update(u << 1 | 1, mid + 1, r, L, R, v);
sum[u] = min(sum[u << 1], sum[u << 1 | 1]) + laz[u];
return;
}
int query(int u,int l,int r) {
if(l == r) return l;
int mid = l + r >> 1;
if(laz[u] + sum[u << 1] == sum[u]) {
return query(u << 1,l,mid);
}else {
return query(u << 1 | 1,mid + 1,r);
}
}
vector<node> st[N], ed[N];
void solve(){
int n,d;
cin >> n >> d;
bool flag = true;
for(int i = 1;i <= n;i ++) {
int x1,x2,y1,y2;
cin >> x1 >> y1 >> x2 >> y2;
// 精度問題
x1 += d * INF,y1 += d * INF, y2 += d * INF, x2 += d * INF;
vector<node> a,b;
if(x2 - x1 >= d && y2 - y1 >= d) {
flag = 0;
}
if(x2 - x1 >= d) {
a.pb({0,d - 1});
}else if((x2 - 1) % d >= x1 % d) {
a.pb({x1 % d, (x2 - 1) % d});
}else {
a.pb({0,(x2 - 1) % d});
a.pb({x1 % d, d - 1});
}
if(y2 - y1 >= d) {
b.pb({0,d-1});
}else if((y2 - 1) % d >= y1 % d) {
b.pb({y1 % d, (y2 - 1) % d});
}else {
b.pb({0,(y2 - 1) % d});
b.pb({y1 % d, d - 1});
}
for(auto sa : a) {
for(auto sb : b) {
st[sa.l].pb(sb);
ed[sa.r + 1].pb(sb);
}
}
}
if(!flag) {
cout << "NO" << endl;
return;
}
for(int i = 0;i < d;i ++) {
// 維護差分
for(auto a : st[i]) {
update(1,0,d-1,a.l,a.r,1);
}
for(auto b : ed[i]) {
update(1,0,d-1,b.l,b.r,-1);
}
if(sum[1] == 0) {
cout << "YES" << endl;
cout << i << ' ' << query(1,0,d-1) << endl;
return;
}
}
cout << "NO" << endl;
}
I. Intervals on the Ring
- 題意
給你一個n,m,序列長度為n且(n,1)為相鄰的,(1,3)表示(1,2,3), (3,1)表示(3,4,5,1), 並且給出m個區間,讓你構造一個k個區間,令這k個區間的交集為給出的m個區間的並集。
- 思路
構造題,考場沒想到簡單的做法,所以寫了賊長
就是類似:
110011001100110011
連續1表示需要交集的部分,那么就這樣構造, (5,2), (9,5),(13,10),(17,14)記錄每一個連續的l,r即可,\(r_i \to l_{i-1}\)即可,最后特判最后一個和第一個是否相連即可。
code:
int a[N];
vpi ans;
void solve(){
int n,m;
cin >> n >> m;
int l,r;
ans.clear();
for(int i = 1;i <= 2 * n;i ++) a[i] = 0;
fep(i,1,m) { // 構建差分數組
cin >> l >> r;
if(l <= r) {
a[l] ++, a[r + 1] --;
}else {
a[l] ++, a[r + n + 1] --;
}
}
if(m == 1) {
cout << "1" << endl << l << ' ' << r << endl;
return;
}
for(int i = 1;i <= 2 * n;i ++) {
a[i] += a[i - 1];
}
// 建立雙指針
l = 1,r = 0;
while(l <= n && !a[l] && !a[l + n]) l ++;
r = l;
while(r <= n && (a[r] || a[r + n])) r ++;
if(r > n) {
cout << "1" << endl << l << ' ' << n << endl;
return;
}
int now = r;
r --;
ans.pb({l,r});
while(now <= n && (!a[now] && !a[now + n])) now ++;
while(now <= n) {
int i = now;
while(i <= n && (a[i] || a[i + n])) i ++;
ans.push_back({now,i - 1});
now = i;
while(now <= n && (!a[now] && !a[now + n])) now ++;
}
if(ans.size() == 1) {
cout << "1" << endl;
cout << ans[0].first << ' ' << ans[0].second << endl;
}else {
if((a[n] || a[n + n]) && (a[1] || a[1 + n])) {
cout << ans.size() - 1 << endl;
for(int i = 1;i < ans.size();i ++) {
cout << ans[i].first << ' ' << ans[i - 1].second << endl;
}
}else {
cout << ans.size() << endl;
for(int i = 1;i < ans.size();i ++) {
cout << ans[i].first << ' ' << ans[i - 1].second << endl;
}
cout << ans[0].first << ' ' << ans[ans.size() - 1].second << endl;
}
}
}
J.Defend Your Country
- 題意
給出一個無向連通圖,每個點的權值為\(w_i\),那么當一個連通塊中的點數為奇數個那么整個聯通塊的價值是-1*權值和,否則是+1 *權值和,讓你通過刪邊來最大化整個圖中的價值
- 思路
- 如果當前整個圖的點都是偶數個,那么答案直接是\(\sum_{1}^{n} w_i\)
- 若為奇數個那么一定是將整個圖變成 偶+1奇+偶 最優,那么令中間的奇數點最小即可。
- 那么首先,這個奇數點是割點,然后它去的邊必須也是偶數的
code:
int n,m;
ll w[N];
int h[N], e[M], ne[M], idx;
int dfn[N],low[N],times;
int sz[N];
ll res = 1e9 + 7;
void add(int a,int b) {
e[idx] = b,ne[idx] = h[a], h[a] = idx ++;
}
void tarjan(int u,int fa = -1) {
dfn[u] = low[u] = ++ times;
sz[u] = 1;
bool ok = 1;
for(int i = h[u];~i;i = ne[i]) {
int j = e[i];
if(j == fa) continue;
if(!dfn[j]) {
tarjan(j,u);
low[u] = min(low[u],low[j]);
if(u == 1) {
// 根節點是割點的情況
if(i != h[u] && sz[j] % 2) ok = 0;
}
else if(dfn[u] <= low[j] && sz[j] % 2) { // 割點
ok = 0;
}
sz[u] += sz[j];
}else low[u] = min(low[u], dfn[j]);
}
if(ok) res = min(res, w[u]);
}
void solve(){
n = rd(), m = rd();
ll sum = 0;
fep(i,1,n) w[i] = rd(), sum += w[i];
idx = times = 0;
res = 1e9 + 7;
for(int i = 1;i <= n;i ++) {
h[i] = -1;
dfn[i] = 0;
sz[i] = 0;
}
fep(i,1,m) {
int a = rd(),b = rd();
add(a,b);
add(b,a);
}
if(n % 2 == 0) {
cout << sum << endl;
return;
}
// 尋找割點,令整個圖分割成(偶奇偶)即可
tarjan(1);
ll ans = max(-sum, sum - 2 * res);
pr("%lld\n", ans);
}