2017CCPC哈爾濱站 題解

A. Palindrome

  • 隊友寫的馬拉車+主席樹,待填坑

B. K-th Number

題意

  • 就是給你一個序列AA,求AA的所有長度大於等於KK的子區間的第KK大數中的第MM
  • 好繞啊,注意第KK大是指從大到小數

題解

  • 直接做不好做啊,本來想算每個數放入貢獻,然後排序,然而無果
  • 顯然第MM大是一個單調的概念,考慮二分,然後去找第KK大大於等於midmid的區間個數,checkcheck的時候只用雙指針掃一下就行了

複雜度

  • O(nlog109)O(n\log 10^9)

代碼

#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);
    }
}

C. Confliction

  • 留坑

D. X-Men

題意

  • 就是一棵樹上有一些人,每個人都會在距離自己大於1的所有人當中隨機選一個,然後朝着他走一步,耗時1,當所有人兩兩間隔都小於等於1的時候遊戲結束,求遊戲進行期望時間

題解

  • 神題!!!出題人的初衷這是一個簽到題,結果最後只過了5個人
  • 啥叫期望?出了數學上的定義,通俗的理解就是平均情況下的狀態,那麼考慮系統達到平衡最快的方式,就是距離最遠的兩個人對向而走,其他人也跟着靠攏,所以期望時間爲最遠點對距離除以二並且向下取整,因爲最後距離是1而不是0

複雜度

  • O(n)O(n)

代碼

#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();
    }
}

F. Permutation

題解

  • 簽到,交叉放即可

代碼

#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':' ');
    }
}

H. A Simple Stone Game

題意

  • 就是有nn堆石頭,每次可以將一堆中的一個石頭放到另一堆中,對應代價爲11,當所有對的石頭數量有一個大於1的公共因子KK的時候遊戲結束,求遊戲結束時的最小代價

題解

  • 首先如果所有石堆都有一個公共因子的話,那麼他們的和sumsum也是這個因子的倍數,枚舉sumsum的質因子,然後對於一個質因子pip_i,將所有ai%pia_i\%p_i排序,從大到小滿足所有數,即讓這個數變成pip_i產生的代價爲pi(ai%pi)p_i-(a_i\%p_i),至於爲什麼不會是2pi2p_i,那麼這種情況下只會是至少3個數拼到一堆,對應的代價爲所有較小的num1num-1之和,那麼還不如合併成多堆大小爲pip_i的數,代價更小

複雜度

  • O(nlognlog109)O(n\log n\log 10^9)

代碼

#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);
    }
}

K. Server

題意

  • 就是給你nn個區間[li,ri][l_i,r_i],每個區間有兩個屬性AABB,然後讓你選擇這些區間的一個自己SS,在所有SS中的區間覆蓋區間[1,t][1,t]的情況下使得下面這個式子最小
    iSAiiSBi\frac{\sum_{i\in S}^{}{A_i}}{\sum_{i\in S}^{}{B_i}}

題解

  • 顯然是一個0101規劃問題,二分之後關鍵在於怎麼checkcheck,令
    iSAiiSBimid\frac{\sum_{i\in S}^{}{A_i}}{\sum_{i\in S}^{}{B_i}}\leq mid

    iSAimid×Bi0\sum_{i\in S}^{}{A_i-mid\times B_i}\leq 0
    那麼對於Aimid×Bi0A_i-mid\times B_i \leq 0的區間直接選,然後對於大於0的區間考慮選擇一些滿足未覆蓋的區間,考慮用線段樹維護做一個dpdp,即用dp[i]dp[i]表示覆蓋區間[1,i][1,i]對應的最小代價,然後對於一個區間[li,ri][l_i,r_i],更新dp[ri]=minj=li1ridp[j]+max(0,Aimid×Bi)dp[r_i]=\min_{j=l_i-1}^{r_i}{dp[j]}+max(0,A_i-mid\times B_i),取maxmax的原因是對於負的區間代價爲0

複雜度

  • O(nlognlogm)O(n \log n \log m)
  • logm\log m指二分

代碼

#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);
    }
}

L. Color a Tree

題意

  • 就是給你一顆根爲11的樹,開始所有節點都是白色的,選擇最少的節點塗成黑色滿足給定的兩種描述,描述如下
    • xix_i爲根的子樹中至少有yiy_i個黑點
    • 除去以xix_i爲根的子樹的所有節點中至少有yiy_i個黑點

題解

  • 首先需要注意到如果黑點個數如果爲kk能滿足所有描述,那麼k+1k+1也一定能滿足所有描述,即問題滿足單調性,考慮二分
  • 對於一個二分的值midmid,嘗試將第二種描述轉化一下,即轉化爲以xix_i爲根的子樹最多有midyimid-y_i個黑點,那麼這樣的話每個節點對應的子樹黑點個數對應一個可行的數量區間,然後做一個簡單的樹形dpdp,即計算滿足所有以節點ii爲根的子樹的所有條件的情況下當前節點的子樹黑點數量可行區間,那麼如果midmid在節點1對應的區間裏,則可行
  • 挺巧妙的

複雜度

  • O(nlogn)O(n\log n)

代碼

#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;
}

M. Geometry Problem

題意

  • 就是給你nn個點,然後讓你求一個圓,使得至少有n2\lceil \frac{n}{2}\rceil個點在圓上

題解

  • 由於至少有一半的點在圓上,又因爲三個不再同一條直線上的點可以確定一個圓,那麼考慮隨機出這三個點,然後O(n)O(n)checkcheck所有點是否在圓上,由於三個點都在構成答案的點集中的概率爲18\frac{1}{8},所以期望隨機次數爲88即可得到答案
  • 打虛擬賽的時候由於最後纔想起來隨機,忘記特判n4n\leq4的情況導致一直隨機而TLETLE,然後交了一發比賽就結束了QWQQWQ

複雜度

  • O(n)O(n)

代碼

#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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章