防線
達達學習數學競賽的時候受盡了同仁們的鄙視,終於有一天…受盡屈辱的達達黑化成爲了黑暗英雄怪獸達達。
就如同中二漫畫的情節一樣,怪獸達達打算毀掉這個世界。
數學競賽界的精英 lqr 打算阻止怪獸達達的陰謀,於是她集合了一支由數學競賽選手組成的超級行動隊。
由於隊員們個個都智商超羣,很快,行動隊便來到了怪獸達達的黑暗城堡的下方。
但是,同樣強大的怪獸達達在城堡周圍佈置了一條“不可越過”的堅固防線。
防線由很多防具組成,這些防具分成了 N 組。
我們可以認爲防線是一維的,那麼每一組防具都分佈在防線的某一段上,並且同一組防具是等距離排列的。
也就是說,我們可以用三個整數 S, E 和 D 來描述一組防具,即這一組防具佈置在防線的 S,S + D,S + 2D,…,S + KD(K∈ Z,S + KD≤E,S + (K + 1)D>E)位置上。
黑化的怪獸達達設計的防線極其精良。如果防線的某個位置有偶數個防具,那麼這個位置就是毫無破綻的(包括這個位置一個防具也沒有的情況,因爲 0 也是偶數)。
只有有奇數個防具的位置有破綻,但是整條防線上也最多隻有一個位置有奇數個防具。
作爲行動隊的隊長,lqr 要找到防線的破綻以策劃下一步的行動。
但是,由於防具的數量太多,她實在是不能看出哪裏有破綻。作爲 lqr 可以信任的學弟學妹們,你們要幫助她解決這個問題。
輸入格式
輸入文件的第一行是一個整數 T,表示有 T 組互相獨立的測試數據。
每組數據的第一行是一個整數 N。
之後 N 行,每行三個整數 Si,Ei,Di,代表第 i 組防具的三個參數,數據用空格隔開。
輸出格式
對於每組測試數據,如果防線沒有破綻,即所有的位置都有偶數個防具,輸出一行 “There’s no weakness.”(不包含引號) 。
否則在一行內輸出兩個空格分隔的整數 P 和 C,表示在位置 P 有 C 個防具。當然 C 應該是一個奇數。
數據範圍
防具總數不多於108,
Si≤Ei,
1≤T≤5,
N≤200000,
0≤Si,Ei,Di≤231−1
輸入樣例:
3
2
1 10 1
2 10 1
2
1 10 1
1 10 1
4
1 10 1
4 4 1
1 5 1
6 10 1
輸出樣例:
1 1
There’s no weakness.
4 3
這題有個非常重要的性質,或者說我們怎麼想到這道題可以用二分來優化的呢?
我們可以發現題目隱含了一個條件:破綻最多有一個,那我們便可以用二分來優化了,如果前半段的和是奇數那我們可以收縮空間[l,mid],反之,如果前半段的和不是奇數我們可以把空間鎖定再右側[mid+1,r]
#include<iostream>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=200010;
struct Seq{
int s,e,d;
};
Seq seq[N];
int n;
ll calc(int x)
{
ll res=0;
for(int i=0;i<n;i++)
{
if(seq[i].s<=x)
{
int s=seq[i].s;
int e=min(seq[i].e,x);
int d=seq[i].d;
res+=(0ll+e-s)/d+1;
}
}
return res;
}
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
int l=0,r=0;
for(int i=0;i<n;i++)
{
scanf("%d%d%d",&seq[i].s,&seq[i].e,&seq[i].d);
r=max(r,seq[i].e);
}
while(l<r)
{
int mid=(ll)l+r>>1;
if(calc(mid)&1) r=mid;
else l=mid+1;
}
ll res=calc(r)-calc(r-1);
if(res&1) printf("%d %lld\n",r,res);
else puts("There's no weakness.");
}
return 0;
}
趕牛入圈
農夫約翰希望爲他的奶牛們建立一個畜欄。
這些挑剔的畜生要求畜欄必須是正方形的,而且至少要包含C單位的三葉草,來當做它們的下午茶。
畜欄的邊緣必須與X,Y軸平行。
約翰的土地裏一共包含N單位的三葉草,每單位三葉草位於一個1 x 1的土地區域內,區域位置由其左下角座標表示,並且區域左下角的X,Y座標都爲整數,範圍在1到10000以內。
多個單位的三葉草可能會位於同一個1 x 1的區域內,因爲這個原因,在接下來的輸入中,同一個區域座標可能出現多次。
只有一個區域完全位於修好的畜欄之中,才認爲這個區域內的三葉草在畜欄之中。
請你幫約翰計算一下,能包含至少C單位面積三葉草的情況下,畜欄的最小邊長是多少。
輸入格式
第一行輸入兩個整數 C 和 N。
接下來 N 行,每行輸入兩個整數 X 和 Y,代表三葉草所在的區域的X,Y座標。
同一行數據用空格隔開。
輸出格式
輸出一個整數,代表畜欄的最小邊長。
數據範圍
1≤C≤500,
C≤N≤500
輸入樣例:
3 4
1 2
2 1
4 1
5 2
輸出樣例:
4
這題用二分+暴力+離散化就過了,這題很容易想到使用二分,但是顯然直接二分+暴力求前綴和是過不去的,10000數據擺在那,我們離散化一下就可以了,來看代碼:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#define pii pair<int,int>
using namespace std;
const int N=1010;
vector<int> v;
pii lawn[N];
int c,n,sum[N][N];
bool calc(int len)
{
for(int x1=0,x2=1;x2<v.size();x2++)
{
while(v[x2]-v[x1+1]+1>len) x1++;
for(int y1=0,y2=1;y2<v.size();y2++)
{
while(v[y2]-v[y1+1]+1>len) y1++;
if(sum[x2][y2]-sum[x1][y2]-sum[x2][y1]+sum[x1][y1]>=c)
return true;
}
}
return false;
}
int get(int x)
{
int l=0,r=v.size()-1;
while(l<r)
{
int mid=l+r>>1;
if(v[mid]>=x) r=mid;
else l=mid+1;
}
return r;
}
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
scanf("%d%d",&c,&n);
int l=1,r=10000;
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
v.push_back(x);
v.push_back(y);
lawn[i]={x,y};
}
v.push_back(0);
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
for(int i=1;i<=n;i++)
{
int x=get(lawn[i].first);
int y=get(lawn[i].second);
sum[x][y]+=1;
}
for(int i=1;i<v.size();i++)
for(int j=1;j<v.size();j++)
sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
while(l<r)
{
int mid=l+r>>1;
if(calc(mid)) r=mid;
else l=mid+1;
}
printf("%d\n",r);
return 0;
}
糖果傳遞
有n個小朋友坐成一圈,每人有a[i]個糖果。
每人只能給左右兩人傳遞糖果。
每人每次傳遞一個糖果代價爲1。
求使所有人獲得均等糖果的最小代價。
輸入格式
第一行輸入一個正整數n,表示小朋友的個數。
接下來n行,每行一個整數a[i],表示第i個小朋友初始得到的糖果的顆數。
輸出格式
輸出一個整數,表示最小代價。
數據範圍
1≤n≤1000000
輸入樣例:
4
1
2
5
4
輸出樣例:
4
這題就是七夕祭那題的一維版本給大家看一下證明:
顯然最後
且
我們化簡
等等,我們可以把所有x都用表示出來
我們不妨暫時先設那麼我們最後代價就可以轉化爲貨艙選址問題,如何選決定了我們最後的代價,找個中位數作爲,我們就可以得到最優解
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
const int N=1000010;
int a[N],d[N];
int n;
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
scanf("%d",&n);
ll sum=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
sum/=n;
for(int i=n;i>=2;i--)
d[i]=sum+d[i+1]-a[i];
d[1]=0;
sort(d+1,d+n+1);
int midv=d[(1+n)>>1];
ll res=0;
for(int i=1;i<=n;i++)
res+=abs(d[i]-midv);
printf("%lld\n",res);
return 0;
}
士兵
格格蘭郡的N名士兵隨機散落在全郡各地。
格格蘭郡中的位置由一對(x,y)整數座標表示。
士兵可以進行移動,每次移動,一名士兵可以向上,向下,向左或向右移動一個單位(因此,他的x或y座標也將加1或減1)。
現在希望通過移動士兵,使得所有士兵彼此相鄰的處於同一條水平線內,即所有士兵的y座標相同並且x座標相鄰。
請你計算滿足要求的情況下,所有士兵的總移動次數最少是多少。
需注意,兩個或多個士兵不能佔據同一個位置。
輸入格式
第一行輸入整數N,代表士兵的數量。
接下來的N行,每行輸入兩個整數x和y,分別代表一個士兵所在位置的x座標和y座標,第i行即爲第i個士兵的座標(x[i],y[i])。
輸出格式
輸出一個整數,代表所有士兵的總移動次數的最小值。
數據範圍
1≤N≤10000,
−10000≤x[i],y[i]≤10000
輸入樣例:
5
1 2
2 2
1 3
3 -2
3 3
輸出樣例:
8
看見這題我們首先就要知道這題的橫縱座標是可以分開計算的。
我們可以先假設每個士兵可以在同一格,那我們怎麼辦,這就是我們的貨艙選址問題,我們直接求就可以了,但這題不允許我們在同一格上怎麼辦呢?
我們可以先按y軸排序,先用貨艙選址問題把他們弄在同一行的代價先算出來。然後我們可以發現,我們只要固定第一個人的位置其他人的位置也確定下來了,還有就是我們可以發現他們的相對位置不變,所以我們給x軸的每個數減去一個等差數列就可以了,距離來看代碼。。~ 。~
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
int x[N],y[N];
int n;
int get(int a[])
{
sort(a+1,a+n+1);
int midv=a[(1+n)>>1];
int res=0;
for(int i=1;i<=n;i++)
res+=abs(a[i]-midv);
return res;
}
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
sort(x+1,x+n+1);
for(int i=1;i<=n;i++)
x[i]-=i-1;
int res=get(x)+get(y);
cout<<res<<endl;
return 0;
}