(2019年暑假牛客訓練第九場)H Cutting Bamboos and J - Symmetrical Painting (主席樹、樹狀數組)

H Cutting Bamboos
題意:給n個竹子,q次詢問,每次l,r,x,y;區間l,人,內將所有竹子砍y次全部砍完,每次從大到小開始砍,每次砍的體積一樣。

主席樹加二分

問x次砍的高度是多少。。

二分第x次砍的高度的是mid,求剩餘的高度與y-x次*(區間和/(r-l+1))作比較即可。。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10,M=1e5+10;
const double esp=1e-7;
ll a[N],sum[N],x,s[20*N],num[20*N];
int ls[20*N],rs[20*N],rt[N];
int n,q;
int cnt;
void up(int pre,int &o,int l,int r,int pos)
{
    o=++cnt;
    ls[o]=ls[pre];
    rs[o]=rs[pre];
    s[o]=s[pre]+pos;
    num[o]=num[pre]+1;
    if(l==r)
    {
        return ;
    }
    int mid=l+r>>1;
    if(pos<=mid) up(ls[pre],ls[o],l,mid,pos);
    else up(rs[pre],rs[o],mid+1,r,pos);
}
ll n1,n2;//n1是大於k的,n2是大於等於k的和
void qu(int pre,int o,int l,int r,int ql,int qr)
{
    /*if(r<ql) return ;
    if(l>=ql)
    {*/
    if(l>qr) return ;
    if(r<=qr)
    {
        n1+=num[o]-num[pre];
        n2+=s[o]-s[pre];
        return ;
    }
    int mid=l+r>>1;
    if(ql<=mid) qu(ls[pre],ls[o],l,mid,ql,qr);
    if(qr>mid) qu(rs[pre],rs[o],mid+1,r,ql,qr);
}
int main()
{
    cin>>n>>q;
    for(int i=1;i<=n;++i)
    {
        scanf("%lld",&x);
        sum[i]=sum[i-1]+x;
        up(rt[i-1],rt[i],1,M,x);
    }
    while(q--)
    {
        int l,r,x,y;
        scanf("%d%d",&l,&r);
        scanf("%d%d",&x,&y);
        double ql=0,qr=100000;
        while(qr-ql>esp)
        {
            double mid=(ql+qr)/2;
            n1=0,n2=0;
            qu(rt[l-1],rt[r],1,M,1,(int)mid);
            //這是求剩餘的高度總和
            n1=r-l+1-n1;
            double ss=n1*mid+n2;
            double step=1.0*(sum[r]-sum[l-1])/y;
            if(step*(y-x)<ss) qr=mid;
            else ql=mid;
            //這是求已經切除的高度總和,wa了
            /*
            double tmp1=n2-n1*mid;
            double step=1.0*(sum[r]-sum[l-1])/y;
            if(step*x>1.0*tmp1) qr=mid;
            else ql=mid;
            */
        }
        printf("%.15f\n",ql);
    }
}


補題之J
這題是聽許老師的思路寫的。思路很妙。。把所有的中位線拿出來離散化。

思路就是枚舉所有中位線爲答案所需中位線,看其他的的矩形對其產生的答案是多少。

當前矩形對其他矩形產生的答案就是下圖紅色部分

 

那麼我就可以用樹狀數組對當前矩形能對其他矩形產生的影響進行區間加。(先找一個最左的標準線)記錄第一個箭頭符號表示當前矩形的右端點的長度。和記錄被影響的中位線出現的次數。。。答案=sum-num*中位線距離。sum是第一個箭頭的區間和,num是當前中位線的次數。

【代碼】

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10,inf=2e9;
typedef long long ll;
struct node
{
    ll l,r,mid;
}a[N];
int len;
ll sum[2][N];
int num[2][N];
ll Y[N];
ll s1,s2;
int n1,n2;
int lowbit(int x)
{
    return x&(-x);
}
void up(int id,int val,int x,int ty)
{
    for(int i=id;i<=len;i+=lowbit(i))
    {
        sum[ty][i]+=val,num[ty][i]+=x;
    }
}
ll qu1(int id,int ty)
{
    ll res=0;
    for(int i=id;i;i-=lowbit(i))
    {
        res+=sum[ty][i];
    }
    return res;
}

ll qu2(int id,int ty)
{
    ll res=0;
    for(int i=id;i;i-=lowbit(i))
    {
        res+=num[ty][i];
    }
    return res;
}
ll T[N];
int getid(ll x)
{
    return lower_bound(Y+1,Y+1+len,x)-Y;
}
int getid1(ll x)
{
    return upper_bound(Y+1,Y+1+len,x)-Y-1;
}

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%lld%lld",&a[i].l,&a[i].r);
        a[i].mid=a[i].l+a[i].r;
        a[i].l*=2;a[i].r*=2;
        Y[++len]=a[i].mid;
    }
    sort(Y+1,Y+1+len);
    len=unique(Y+1,Y+1+len)-Y-1;
    for(int i=1;i<=n;++i)
    {
        int ql=getid(a[i].l);
        int k=getid(a[i].mid);
        int qr=getid1(a[i].r);
        T[k]+=a[i].r-a[i].l;
        ll val=a[i].r;
        up(k,val,1,0);
        up(qr+1,-val,-1,0);

        val=inf-a[i].l;
        up(ql,val,1,1);
        up(k+1,-val,-1,1);
    }
    ll ans=0;
    for(int i=1;i<=len;++i)
    {
        s1=0,n1=0,s2=0,n2=0;
        int pos=i;
        ll t1=qu1(pos,0)-qu2(pos,0)*Y[i];
        ll t2=qu1(pos,1)-qu2(pos,1)*(inf-Y[i]);
        ll tmp=t1+t2;
        ans=max(ans,tmp-T[i]/2);
    }
    printf("%lld\n",ans);
}


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章