T1 Closest Cow Wins
題目
Farmer John owns a long farm along the highway that can be considered somewhat
like a one-dimensional number line. Along the farm, there are \(K\) grassy
patches (\(1 \leq K \leq 2\cdot 10^5\)); the \(i\)-th patch is located at position
\(p_i\) and has an associated tastiness value \(t_i\) (\(0\le t_i\le 10^9\)).
Farmer John's nemesis, Farmer Nhoj, has already situated his \(M\) cows
(\(1 \leq M \leq 2\cdot 10^5\)) at locations \(f_1 \ldots f_M\). All \(K+M\) of these
locations are distinct integers in the range \([0,10^9]\).
Farmer John needs to pick \(N\) (\(1\le N\le 2\cdot 10^5\)) locations (not
necessarily integers) for his cows to be located. These must be distinct from
those already occupied by the cows of Farmer Nhoj, but it is possible for
Farmer John to place his cows at the same locations as grassy patches.
Whichever farmer owns a cow closest to a grassy patch can claim ownership of
that patch. If there are two cows from rival farmers equally close to the
patch, then Farmer Nhoj claims the patch.
Given the locations of Farmer Nhoj's cows and the locations and tastiness values
of the grassy patches, determine the maximum total tastiness Farmer John's cows
can claim if optimally positioned.
Farmer John 沿着一條高速公路擁有一個很長的農場,可以被看作類似於一維數軸。沿着農場有 K 塊草地(1≤K≤2⋅105);第 i 塊草地位於位置 pi 並具有美味值 ti(0≤ti≤109)。Farmer John 的死對頭 Farmer Nhoj 已經將他的 M 頭奶牛(1≤M≤2⋅105)放在了位置 f1…fM 。所有這些 K+M 個位置均是 [0,109] 范圍內的不同整數。
Farmer John 需要選擇 N
(1≤N≤2⋅105
)個位置(不一定是整數)放置他的奶牛。這些位置必須與 Farmer Nhoj 的奶牛已經占用的位置不同,但是 Farmer John 可以將他的奶牛放在與草地相同的位置。
擁有最靠近某個草地的奶牛的農夫擁有這一草地。如果來自兩方農夫的兩頭奶牛距這一草地相等,則 Farmer Nhoj 擁有該草地。
給定 Farmer Nhoj 的奶牛的位置以及草地的位置和美味值,求 Farmer John 的奶牛以最優方式放置時可以達到的最大總美味值。
思路
考慮在別人的兩頭奶牛之間放置多少頭奶牛。
首先我們可以明確,在FN的任意兩頭奶牛之間,只能放0/1/2頭奶牛。放0頭相當於沒有,放2頭相當於拿走中間所以美味值,難點是放1頭。
對於放一頭的情況,這頭奶牛必然可以且剛好拿到一半的位置,於是我們可以用雙指針維護。當這一半位置的左端點移動時,右端點也跟着移動。
最后我們采用一個優先隊列來維護。先把所有1頭奶牛的情況丟到一個大根堆里,然后每次把1頭奶牛pop出去后再把這個區間內2頭減1頭的數值塞到堆里即可。
總結
對於此類問題,我們可以分區間考慮。對於一些難處理的情況可以采用雙指針解決。同時,對於答案可以通過優先隊列來維護。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 200010
struct Node
{
int x, y;
bool operator <(const Node &A) const { return x<A.x; }
}t;
struct node { int x, y; }a[N];
int n, m, i, j, k;
priority_queue<Node>q;
int op, opx, s[N], f[N], ans, nw;
int len, nx, nlen, l, r, id;
bool cmp(node x, node y) { return x.x<y.x; }
int findx(int x) {
if(a[n].x<x) return 1e17;
int l=1, r=n, mid;
while(l<r) {
mid=l+r>>1;
if(a[mid].x<x) l=mid+1;
else r=mid;
}
return l;
}
int findy(int x) {
if(a[1].x>x) return -1e17;
int l=1, r=n, mid;
while(l<r) {
mid=l+r+1>>1;
if(a[mid].x<x) l=mid;
else r=mid-1;
}
return l;
}
signed main() {
scanf("%lld%lld%lld", &n, &m, &k);
for(i=1; i<=n; ++i)
scanf("%lld%lld", &a[i].x, &a[i].y), a[i].x*=2;
sort(a+1, a+n+1, cmp);
for(i=1; i<=n; ++i) s[i]=s[i-1]+a[i].y;
for(i=1; i<=m; ++i) scanf("%lld", &f[i]), f[i]*=2;
sort(f+1, f+m+1);
f[0]=-1e17; f[m+1]=1e17;
for(id=0; id<=m; ++id) {
l=findx(f[id]); r=findy(f[id+1]);
if(l>r) continue;
len=f[id+1]-f[id]; nw=0;
for(i=j=l; i<=r; ++i) {
for(; j<=r; ++j)
if((a[j].x-a[i].x)*2>=len) break;
nw=max(nw, s[j-1]-s[i-1]);
}
t.x=nw; t.y=s[r]-s[l-1]-t.x; q.push(t);
}
for(i=1; i<=k; ++i) {
t=q.top(); q.pop();
ans+=t.x; t.x=t.y; t.y=0;
q.push(t);
}
printf("%lld", ans);
return 0;
}
T2 Connecting Two Barns
題目
Farmer John's farm consists of a set of \(N\) fields \((1 \leq N \leq 10^5)\),
conveniently numbered \(1 \ldots N\). Between these fields are \(M\) bi-directed
paths \((0 \leq M \leq 10^5)\), each connecting a pair of fields.
The farm contains two barns, one in field 1 and the other in field \(N\). Farmer
John would like to ensure that there is a way to walk between the two barns
along some series of paths. He is willing to build up to two new paths to
accomplish this goal. Due to the way the fields are situated, the cost of
building a new path between fields \(i\) and \(j\) is \((i-j)^2\).
Please help Farmer John determine the minimum cost needed such that barns \(1\)
and \(N\) become reachable from each-other.
Farmer John 的農場由 \(N\) 塊田地(\(1 \leq N \leq 10^5\))組成,編號為 \(1 \ldots N\)。在這些田地之間有 \(M\) 條雙向道路(\(0 \leq M \leq 10^5\)),每條道路連接兩塊田地。
農場有兩個牛棚,一個在田地 1 中,另一個在田地 \(N\) 中。Farmer John 希望確保有一種方式可以沿着一組道路在兩個牛棚之間行走。 他願意建造至多兩條新道路來實現這一目標。由於田地的位置因素,在田地 \(i\) 和 \(j\) 之間建造新道路的花費是 \((i-j)^2\)。
請幫助 Farmer John 求出使得牛棚 \(1\) 和 \(N\) 可以相互到達所需要的最小花費。
思路
由於只能連兩條邊,所以只有兩種情況。
-
在1號田所在的塊與 \(n\) 號田所在的塊直接相連。
-
通過一個中轉塊,在1號田所在的塊與 \(n\) 號田所在的塊與這個中轉塊相連。
那么相連的最小代價是多少呢?
我們可以枚舉期中一個塊中的每一個點,然后在對應塊中二分找出剛好大於和剛好小於的點,算出后去個min值即可。
要注意的是,我們要枚舉中轉塊的點,因為這最多只有 \(n\) 個點,只會二分 \(n\) 次。枚舉1號塊或 \(n\) 號塊的點容易超時。
可以用並查集和set做。
總結
對於題目一些給定且過小的性質,我們可以直接考慮情況。
在統計最小的過程中,可以二分上下界。
對於一些過大的枚舉,可以反過來,就會變得很小。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 100010
int n, m, i, j, k;
set<int>s[N];
set<int>::iterator it, jt, kt;
int f[N], p[N], u, v, a, b, ans, t;
int fa(int x) { return f[x]==x ? x : f[x]=fa(f[x]); }
int pingfang(int x) { return x*x; }
int len(int x, int y) {
int ans=1e18;
for(it=s[x].begin(); it!=s[x].end(); ++it) {
jt=s[y].lower_bound(*it);
if(jt!=s[y].end()) ans=min(ans, pingfang((*jt)-*it));
if(jt!=s[y].begin()) ans=min(ans, pingfang((*(--jt))-*it));
}
return ans;
}
signed main()
{
scanf("%lld", &t);
while(t--)
{
scanf("%lld%lld", &n, &m);
for(i=1; i<=n; ++i) f[i]=i;
for(i=1; i<=n; ++i) s[i].clear();
for(i=1; i<=m; ++i)
scanf("%lld%lld", &u, &v), f[fa(u)]=fa(v);
for(i=1; i<=n; ++i) s[fa(i)].insert(i);
a=f[1]; b=f[n]; ans=len(a, b);
for(i=1; i<=n; ++i)
{
if(f[i]==a||f[i]==b||f[i]!=i) continue;
ans=min(ans, len(i, a)+len(i, b));
}
printf("%lld\n", ans);
}
return 0;
}
T3 Convoluted Intervals
題目
The cows are hard at work trying to invent interesting new games to play. One of
their current endeavors involves a set of \(N\) intervals
(\(1\le N\le 2\cdot 10^5\)), where the \(i\)th interval starts at position \(a_i\) on
the number line and ends at position \(b_i \geq a_i\). Both \(a_i\) and \(b_i\) are
integers in the range \(0 \ldots M\), where \(1 \leq M \leq 5000\).
To play the game, Bessie chooses some interval (say, the \(i\)th interval) and her
cousin Elsie chooses some interval (say, the \(j\)th interval, possibly the same
as Bessie's interval). Given some value \(k\), they win if
\(a_i + a_j \leq k \leq b_i + b_j\).
For every value of \(k\) in the range \(0 \ldots 2M\), please count the number of
ordered pairs \((i,j)\) for which Bessie and Elsie can win the game.
奶牛們正在努力嘗試發明有趣的新游戲來玩。他們目前的工作之一與一組 \(N\) 個區間(\(1\le N\le 2\cdot 10^5\))有關,其中第 \(i\) 個區間從數軸上的 \(a_i\) 位置開始,並在位置 \(b_i \geq a_i\) 結束。\(a_i\) 和 \(b_i\) 均為 \(0 \ldots M\) 范圍內的整數,其中 \(1 \leq M \leq 5000\)。
這個游戲的玩法是,Bessie 選擇某個區間(假設是第 \(i\) 個區間),而她的表妹 Elsie 選擇某個區間(假設是第 \(j\) 個區間,可能與 Bessie 所選的的區間相同)。給定某個值 \(k\),如果 \(a_i + a_j \leq k \leq b_i + b_j\),則她們獲勝。
對范圍 \(0 \ldots 2M\) 內的每個值 \(k\),請計算使得 Bessie 和 Elsie 可以贏得游戲的有序對 \((i,j)\) 的數量。
思路
用差分思想考慮,所有 \(a_i+a_j\) 的地方+1,所以 \(b_i+b_j+1\) 的地方減1。
然而這是 \(O(n^2)\),但我們發現 \(a,b\) 的值域很小,所以我們可以用桶存起來,然后按值域遍歷即可,就變成 \(O(m^2)\)。
總結
對於一些過於重復的計算,如果值域很小,我們可以嘗試用桶存起來,減去很多過於重復的計算。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 5010
int n, m, i, j, k;
int a[N], b[N], x[N], y[N], c[N*2];
signed main()
{
scanf("%lld%lld", &n, &m);
for(i=1; i<=n; ++i) scanf("%lld%lld", &j, &k), x[j]++, y[k]++;
for(i=0; i<=m; ++i)
for(j=0; j<=m; ++j)
c[i+j]+=x[i]*x[j], c[i+j+1]-=y[i]*y[j];
for(i=k=0; i<=2*m; ++i) printf("%lld\n", k+=c[i]);
return 0;
}