題意
- 就是給你一個序列A,求A的所有長度大於等於K的子區間的第K大數中的第M大
- 好繞啊,注意第K大是指從大到小數
題解
- 直接做不好做啊,本來想算每個數放入貢獻,然後排序,然而無果
- 顯然第M大是一個單調的概念,考慮二分,然後去找第K大大於等於mid的區間個數,check的時候只用雙指針掃一下就行了
複雜度
- O(nlog109)
代碼
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,k,a[maxn];
long long m;
bool check(int x) {
int p=0,sum=0;
long long res=0;
for(int i=1;i<=n;i++) {
while(p<=n&&sum<k) if(a[++p]>=x) sum++;
res+=n-p+1;
if(a[i]>=x) sum--;
}
return res>=m;
}
int main() {
int t;scanf("%d",&t);
while(t--) {
scanf("%d %d %lld",&n,&k,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int l=1,r=1000000000,ans;
while(l<=r) {
int mid=(l+r)>>1;
if(check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
}
題意
- 就是一棵樹上有一些人,每個人都會在距離自己大於1的所有人當中隨機選一個,然後朝着他走一步,耗時1,當所有人兩兩間隔都小於等於1的時候遊戲結束,求遊戲進行期望時間
題解
- 神題!!!出題人的初衷這是一個簽到題,結果最後只過了5個人
- 啥叫期望?出了數學上的定義,通俗的理解就是平均情況下的狀態,那麼考慮系統達到平衡最快的方式,就是距離最遠的兩個人對向而走,其他人也跟着靠攏,所以期望時間爲最遠點對距離除以二並且向下取整,因爲最後距離是1而不是0
複雜度
代碼
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,k,a[maxn];
vector<int> vec[maxn];
pair<int,int> dfs(int cur,int fa,int dis)
{
pair<int,int> res=make_pair(0,0);
if(a[cur]) res={dis,cur};
for(int i=0;i<vec[cur].size();i++) if(vec[cur][i]!=fa){
auto k=dfs(vec[cur][i],cur,dis+1);
if(k.first>res.first) res=k;
}
return res;
}
int main()
{
int t;scanf("%d",&t);
while(t--) {
scanf("%d %d",&n,&k);
for(int i=1,u;i<=k;i++) scanf("%d",&u),a[u]=1;
for(int i=1,u,v;i<n;i++) {
scanf("%d %d",&u,&v);
vec[u].push_back(v);
vec[v].push_back(u);
}
auto from=dfs(1,0,0);
auto to=dfs(from.second,0,0);
printf("%.2lf\n",(double)(to.first/2));
for(int i=1;i<=n;i++) a[i]=0,vec[i].clear();
}
}
題解
代碼
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,ans[maxn];
int main() {
int t;scanf("%d",&t);
while(t--) {
scanf("%d",&n);
int qwq=0;
for(int i=1;i<=n;i+=2) ans[i]=++qwq;
for(int i=2;i<=n;i+=2) ans[i]=++qwq;
for(int i=1;i<=n;i++) printf("%d%c",ans[i],i==n?'\n':' ');
}
}
題意
- 就是有n堆石頭,每次可以將一堆中的一個石頭放到另一堆中,對應代價爲1,當所有對的石頭數量有一個大於1的公共因子K的時候遊戲結束,求遊戲結束時的最小代價
題解
- 首先如果所有石堆都有一個公共因子的話,那麼他們的和sum也是這個因子的倍數,枚舉sum的質因子,然後對於一個質因子pi,將所有ai%pi排序,從大到小滿足所有數,即讓這個數變成pi產生的代價爲pi−(ai%pi),至於爲什麼不會是2pi,那麼這種情況下只會是至少3個數拼到一堆,對應的代價爲所有較小的num−1之和,那麼還不如合併成多堆大小爲pi的數,代價更小
複雜度
- O(nlognlog109)
代碼
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,a[maxn],b[maxn];
long long solve(long long k) {
long long sum=0,ans=0;
for(int i=1;i<=n;i++) b[i]=a[i]%k,sum+=b[i];
sort(b+1,b+n+1);
for(int i=n;i>=1&&sum>0;i--,sum-=k) ans+=k-b[i];
return ans;
}
int main() {
int t;scanf("%d",&t);
while(t--) {
scanf("%d",&n);long long sum=0,ans=1e18;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];
for(int i=2;1LL*i*i<=sum;i++) {
if(sum%i==0) {
ans=min(ans,solve(i));
while(sum%i==0) sum/=i;
}
}
if(sum>1) ans=min(ans,solve(sum));
printf("%lld\n",ans);
}
}
題意
- 就是給你n個區間[li,ri],每個區間有兩個屬性A和B,然後讓你選擇這些區間的一個自己S,在所有S中的區間覆蓋區間[1,t]的情況下使得下面這個式子最小
∑i∈SBi∑i∈SAi
題解
- 顯然是一個01規劃問題,二分之後關鍵在於怎麼check,令
∑i∈SBi∑i∈SAi≤mid
得
i∈S∑Ai−mid×Bi≤0
那麼對於Ai−mid×Bi≤0的區間直接選,然後對於大於0的區間考慮選擇一些滿足未覆蓋的區間,考慮用線段樹維護做一個dp,即用dp[i]表示覆蓋區間[1,i]對應的最小代價,然後對於一個區間[li,ri],更新dp[ri]=minj=li−1ridp[j]+max(0,Ai−mid×Bi),取max的原因是對於負的區間代價爲0
複雜度
- O(nlognlogm)
- logm指二分
代碼
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
#define eps 1e-4
int n,t,m;
struct data{
int s,t,a,b;
friend bool operator<(const data &u,const data &v) {
return u.t==v.t?u.s<v.s:u.t<v.t;
}
}o[maxn];
namespace segment_tree{
double minn[maxn<<2];
inline void update(int id,int L,int R,int pos,double k) {
if(L==R) {minn[id]=min(minn[id],k);return;}
int mid=(L+R)>>1;
if(pos<=mid) update(id<<1,L,mid,pos,k);
else update(id<<1|1,mid+1,R,pos,k);
minn[id]=min(minn[id<<1],minn[id<<1|1]);
}
inline double query(int id,int L,int R,int l,int r) {
double res=0x3f3f3f3f;
if(l<=L&&R<=r) return minn[id];
int mid=(L+R)>>1;
if(l<=mid) res=min(res,query(id<<1,L,mid,l,r));
if(r>mid) res=min(res,query(id<<1|1,mid+1,R,l,r));
return res;
}
}
using namespace segment_tree;
bool check(double mid) {
double neg=0;
for(int i=1;i<=n;i++) if(o[i].a-mid*o[i].b<0) neg+=o[i].a-mid*o[i].b;
for(int i=1;i<=4*m;i++) minn[i]=0x3f3f3f3f;
update(1,0,m,0,0);
for(int i=1;i<=n;i++) {
double now=query(1,0,m,o[i].s-1,o[i].t-1)+max(0.0,o[i].a-mid*o[i].b);
update(1,0,m,o[i].t,now);
}
return neg+query(1,0,m,m,m)<=0;
}
int main() {
int t;scanf("%d",&t);
while(t--) {
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d %d %d %d",&o[i].s,&o[i].t,&o[i].a,&o[i].b);
sort(o+1,o+n+1);
double l=0,r=2000;
while(r-l>eps) {
double mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
printf("%.3lf\n",r);
}
}
題意
- 就是給你一顆根爲1的樹,開始所有節點都是白色的,選擇最少的節點塗成黑色滿足給定的兩種描述,描述如下
- 以xi爲根的子樹中至少有yi個黑點
- 除去以xi爲根的子樹的所有節點中至少有yi個黑點
題解
- 首先需要注意到如果黑點個數如果爲k能滿足所有描述,那麼k+1也一定能滿足所有描述,即問題滿足單調性,考慮二分
- 對於一個二分的值mid,嘗試將第二種描述轉化一下,即轉化爲以xi爲根的子樹最多有mid−yi個黑點,那麼這樣的話每個節點對應的子樹黑點個數對應一個可行的數量區間,然後做一個簡單的樹形dp,即計算滿足所有以節點i爲根的子樹的所有條件的情況下當前節點的子樹黑點數量可行區間,那麼如果mid在節點1對應的區間裏,則可行
- 挺巧妙的
複雜度
- O(nlogn)
代碼
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
vector<int> vec[maxn];
int n,m,k,x[2][maxn],y[2][maxn],l[maxn],r[maxn],siz[maxn];
void dfs_size(int cur,int fa) {
siz[cur]=1;
for(int i=0;i<vec[cur].size();i++) if(vec[cur][i]!=fa) {
dfs_size(vec[cur][i],cur);
siz[cur]+=siz[vec[cur][i]];
}
}
void dfs(int cur,int fa) {
int L=0,R=0;
for(int i=0;i<vec[cur].size();i++) if(vec[cur][i]!=fa) {
dfs(vec[cur][i],cur);
L+=l[vec[cur][i]],R+=r[vec[cur][i]];
}
l[cur]=max(l[cur],L),r[cur]=min(r[cur],R+1);
}
bool check(int val) {
for(int i=1;i<=n;i++) l[i]=0,r[i]=siz[i];
for(int i=1;i<=m;i++) l[x[0][i]]=max(l[x[0][i]],y[0][i]);
for(int i=1;i<=k;i++) r[x[1][i]]=min(r[x[1][i]],val-y[1][i]);
dfs(1,0);
for(int i=1;i<=n;i++) if(l[i]>r[i]) return false;
return l[1]<=val&&val<=r[1];
}
int main() {
int t;scanf("%d",&t);
while(t--) {
scanf("%d",&n);
for(int i=1,u,v;i<n;i++) {
scanf("%d %d",&u,&v);
vec[u].push_back(v);
vec[v].push_back(u);
}
dfs_size(1,0);
scanf("%d",&m);
bool ok=true;
for(int i=1;i<=m;i++) {
scanf("%d %d",&x[0][i],&y[0][i]);
if(y[0][i]>siz[x[0][i]]) ok=false;
}
scanf("%d",&k);
for(int i=1;i<=k;i++) {
scanf("%d %d",&x[1][i],&y[1][i]);
if(y[1][i]>n-siz[x[1][i]]) ok=false;
}
if(!ok) {
printf("-1\n");
for(int i=1;i<=n;i++) vec[i].clear();
continue;
}
int L=0,R=n,ans=-1;
while(L<=R) {
int mid=(L+R)>>1;
if(check(mid)) R=mid-1,ans=mid;
else L=mid+1;
}
printf("%d\n",ans);
for(int i=1;i<=n;i++) vec[i].clear();
}
return 0;
}
題意
- 就是給你n個點,然後讓你求一個圓,使得至少有⌈2n⌉個點在圓上
題解
- 由於至少有一半的點在圓上,又因爲三個不再同一條直線上的點可以確定一個圓,那麼考慮隨機出這三個點,然後O(n)去check所有點是否在圓上,由於三個點都在構成答案的點集中的概率爲81,所以期望隨機次數爲8即可得到答案
- 打虛擬賽的時候由於最後纔想起來隨機,忘記特判n≤4的情況導致一直隨機而TLE,然後交了一發比賽就結束了QWQ
複雜度
代碼
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
#define eps 1e-4
#define pi acos(-1.0)
int sgn(double k) {return k<-eps?-1:(k<eps?0:1);}
struct point{
double x,y;
point(double a=0,double b=0) {x=a;y=b;}
point operator*(double k) {return point(x*k,y*k);}
double operator*(point other) {return x*other.x+y*other.y;}
double operator^(point other) {return x*other.y-y*other.x;}
point operator/(double k) {return point(x/k,y/k);}
point operator+(point other) {return point(x+other.x,y+other.y);}
point operator-(point other) {return point(x-other.x,y-other.y);}
friend double len(point p) {return sqrt(p.x*p.x+p.y*p.y);}
friend point rotate(point p1,point p2,double a) {
point vec=p2-p1;
double xx=vec.x*cos(a)+vec.y*sin(a);
double yy=vec.y*cos(a)-vec.x*sin(a);
return point(p1.x+xx,p1.y+yy);
}
}p[maxn];
struct line{
point s,e;
line(){}
line(point a,point b) {s=a;e=b;}
friend point intersect(line l1,line l2) {
double k=((l2.e-l2.s)^(l2.s-l1.s)/((l2.e-l2.s)^(l1.e-l1.s)));
return l1.s+(l1.e-l1.s)*k;
}
};
struct circle{
point o;
double r;
circle() {}
circle(point a,point b,point c) {
point p1=rotate((a+b)/2,b,3*pi/2),p2=rotate((b+c)/2,c,3*pi/2);
o=intersect(line((a+b)/2,p1),line((b+c)/2,p2));
r=len(o-a);
}
};
int n;
int main() {
srand(998244353);
int t;scanf("%d",&t);
while(t--) {
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lf %lf",&p[i].x,&p[i].y);
circle res;
if(n==1) {
printf("%.10lf %.10lf %.10lf\n",p[1].x,p[1].y,0);
continue;
}
else if(n<=4) {
printf("%.10lf %.10lf %.10lf\n",(p[1].x+p[2].x)/2,(p[1].y+p[2].y)/2,len(p[2]-p[1])/2);
continue;
}
for(;;) {
int x=rand()%n+1,y=rand()%n+1,z=rand()%n+1;
if(x==y||x==z||y==z||(sgn((p[y]-p[x])^(p[z]-p[x]))==0)) continue;
circle o=circle(p[x],p[y],p[z]);
int cnt=0;
for(int i=1;i<=n;i++) if(sgn(o.r-len(p[i]-o.o))==0) cnt++;
if(cnt>=(n+1)/2 &&fabs(o.o.x)<=1e9 &&fabs(o.o.y)<=1e9 &&fabs(o.r)<1e9) {res=o;break;}
}
printf("%.10lf %.10lf %.10lf\n",res.o.x,res.o.y,res.r);
}
}