省队集训Round2 DAY7

T2

这里写图片描述
这里写图片描述

题解

考虑最暴力的做法,实际上就是枚举选中区间的左右端点,然后用点分判断选出的点构成的合法路径和未选中的点构成的合法路径的大小。
如果再优化一点可以,枚举其中一个端点,另一个端点动态的维护,有点类似动态点分的样子。
这么做的瓶颈在于枚举端点。首先如果右端点如果右移的话,那么满足条件的区间左端点的位置实际上是单调不降的。如果我们能预处理出某个东西,然后O(1)的判断, 那么问题就解决了。
首先如果是工业点权值为1,农业为-1。那么如果一条路径的点权和为0,那么就是一条合法路径。利用点分治,可以求出ans[i]。ans[i]表示以点i为端点的路径中有多少是合法路径。
那么一个区间的答案能否直接表示成ans[r]-ans[l-1]呢?
这里写图片描述
ans[r]ans[l1] 会得到上图三种合法路径,但是如果选中的是[l,r]那么只有1是合法的。
但是我们发现2,3两种路径在计算选中区间和未选中区间答案的时候都被计算了一次,所以可以直接比较 ans[r]ans[l1] SUM(ans[r]ans[l1]) 大小。再利用单调性就可以 O(n) 的统计答案了

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 300005
#define LL long long
#define inf 1000000000 
using namespace std;
LL cnt[N],ans[N];
int dis[N],n,a[N],size[N],f[N],sum,point[N],nxt[N],v[N];
int tail,q[N],root,vis[N],tot;
void add(int x,int y)
{
    tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
    tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void getroot(int x,int fa)
{
    size[x]=1; f[x]=0;
    for (int i=point[x];i;i=nxt[i]){
        if (v[i]==fa||vis[v[i]]) continue;
        getroot(v[i],x);
        size[x]+=size[v[i]];
        f[x]=max(f[x],size[v[i]]);
    }
    f[x]=max(f[x],sum-size[x]);
    if (f[x]<f[root]) root=x;
}
void dfs(int x,int fa,int d)
{
    d+=a[x]; dis[x]=d;
    q[++tail]=x; cnt[n+d]++;
    for (int i=point[x];i;i=nxt[i])
     if (v[i]!=fa&&!vis[v[i]]) dfs(v[i],x,d);
}
void calc(int opt,int x)
{
    for (int i=1;i<=tail;i++)
     ans[q[i]]+=(LL)opt*cnt[n-dis[q[i]]+a[x]];
    for (int i=1;i<=tail;i++) cnt[n+dis[q[i]]]=0;
    tail=0;
}
void solve(int x)
{
    vis[x]=1;
    dfs(x,0,0); calc(1,x);
    for (int i=point[x];i;i=nxt[i]){
        if (vis[v[i]]) continue;
        dfs(v[i],x,a[x]);
        calc(-1,x);
    }
    for (int i=point[x];i;i=nxt[i]){
        if (vis[v[i]]) continue;
        sum=size[v[i]]; root=0;
        getroot(v[i],x); solve(root);
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
     scanf("%d",&a[i]),a[i]=(a[i]>0?1:-1);
    for (int i=1;i<n;i++) {
        int x,y; scanf("%d%d",&x,&y);
        add(x,y);
    }
    root=0; f[root]=inf; sum=n;
    getroot(1,0);
    solve(root);
    LL total=0; LL num=0; LL now=0;
    for (int i=1;i<=n;i++) total+=ans[i];
    int l=1; 
    for (int i=1;i<=n;i++){
        now+=ans[i];
        while (now>total-now) now-=ans[l],l++;
        num+=(LL)l-1;
    }
    printf("%lld\n",num);
} 


T3

这里写图片描述
这里写图片描述

题解

这道题一上来的感觉就是CDQ分治,关键是怎么统计矩形中不同矩形的个数。
有两个思路:
思路1:来自出题人。
统计矩形中的不容易统计,于是我们转化成统计矩形外的。把矩形外的区域分成八部分计算,即 x2[i]<x1+x1[i]>x2+y2[i]<y1+y1[i]>y2x2[i]<x1andy2[i]<y1x2[i]<x1andy1[i]>y2x1[i]>x2andy2[i]<y1x1[i]>x2andy1[i]>y2
其中一位的可以直接统计,二维的其实就是(时间,x,y)用CDQ求三维偏序。
思路2:
把矩形的左边界和右边界分开考虑。一个矩形中与他相交的矩形个数其实就是他右边界左边所有范围[y1,y2]中左边界的个数-左边界左边所有范围[y1,y2]中右边界的个数。
考虑如何维护左边界的个数。我们可以统计区间中所有的左边界,然后减去全部在y1上方的矩形和全部在y2下方的矩形。这个某一个位置上下方的矩形个数可以用线段树或者树状数组维护。
右边界也是同样的道理。
直接一遍CDQ就可以了。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 500003
using namespace std;
int cnty,cntx,X[N],Y[N],sz,cnt,n;
struct seg{
    int tr[N*4],delta[N*4],lazy[N*4];
    void change(int now,int v){
        tr[now]+=v; delta[now]+=v;
    }
    void clear(int now){
        tr[now]=0; delta[now]=0;
        lazy[now]=1;
    }
    void pushdown(int now){
        if(lazy[now]) {
            clear(now<<1);
            clear(now<<1|1);
            lazy[now]=0;
        }
        if (delta[now]) {
            change(now<<1,delta[now]);
            change(now<<1|1,delta[now]);
            delta[now]=0;
        }
    }
    void qjchange(int now,int l,int r,int ll,int rr,int v){
        if (ll<=l&&r<=rr) {
            change(now,v);
            return;
        }
        pushdown(now);
        int mid=(l+r)/2;
        if (ll<=mid) qjchange(now<<1,l,mid,ll,rr,v);
        if (rr>mid) qjchange(now<<1|1,mid+1,r,ll,rr,v);
    }
    int fp(int now,int l,int r,int x){
        if (x<=0||x>cnty) return 0;  
        if (l==r) return tr[now];
        int mid=(l+r)/2;
        pushdown(now);
        if (x<=mid) return fp(now<<1,l,mid,x);
        else return fp(now<<1|1,mid+1,r,x);
    }
}upl,downl,upr,downr;
struct data{
    int x,l,r,opt,pd,num,ans;
}q[N];
struct node{
    int x1,x2,y1,y2;
}a[N];
int cmp(data a,data b){
    return a.x<b.x||a.x==b.x&&a.opt<b.opt;
}
int cmp1(data a,data b){
    return a.num<b.num||a.num==b.num&&a.opt<b.opt;
}
void cdq(int l,int r)
{
    if (l==r) return;
    int mid=(l+r)/2;
    for (int i=l;i<=mid;i++) q[i].pd=1;
    for (int i=mid+1;i<=r;i++) q[i].pd=0;
    sort(q+l,q+r+1,cmp);
    upl.clear(1); upr.clear(1); downl.clear(1); downr.clear(1); 
    int cntl=0; int cntr=0;
    for (int i=l;i<=r;i++) {
        if (q[i].pd==1){
            if (q[i].opt==1) {
                cntl++;
                upl.qjchange(1,1,cnty,q[i].r,cnty,1);
                downl.qjchange(1,1,cnty,1,q[i].l,1);
            }
            if (q[i].opt==2) {
                cntr++;
                upr.qjchange(1,1,cnty,q[i].r,cnty,1);
                downr.qjchange(1,1,cnty,1,q[i].l,1);
            }
            if (q[i].opt==3) {
                cntl--;
                upl.qjchange(1,1,cnty,q[i].r,cnty,-1);
                downl.qjchange(1,1,cnty,1,q[i].l,-1);
            }
            if (q[i].opt==4) {
                cntr--;
                upr.qjchange(1,1,cnty,q[i].r,cnty,-1);
                downr.qjchange(1,1,cnty,1,q[i].l,-1);
            }
        }
        if (q[i].pd==0){
            if (q[i].opt==5) {
                q[i].ans+=cntr-upr.fp(1,1,cnty,q[i].l-1)-downr.fp(1,1,cnty,q[i].r+1);
            }
            if (q[i].opt==6) {
                q[i].ans+=cntl-upl.fp(1,1,cnty,q[i].l-1)-downl.fp(1,1,cnty,q[i].r+1);
            }
        }
    }
    sort(q+l,q+r+1,cmp1);
    cdq(l,mid); cdq(mid+1,r);
}
int main()
{
    freopen("loads.in","r",stdin);
    freopen("my.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) {
        char s[10]; scanf("%s",s+1);
        if (s[1]=='I') {
            ++cnt; 
            scanf("%d%d%d%d",&a[cnt].x1,&a[cnt].y1,&a[cnt].x2,&a[cnt].y2);
            Y[++cnty]=a[cnt].y1; Y[++cnty]=a[cnt].y2;
            X[++cntx]=a[cnt].x1; X[++cntx]=a[cnt].x2;
            q[++sz].x=a[cnt].x1; q[sz].num=i;
            q[sz].l=a[cnt].y1; q[sz].r=a[cnt].y2;
            q[sz].opt=1;
            q[++sz].x=a[cnt].x2;
            q[sz].l=a[cnt].y1; q[sz].r=a[cnt].y2;
            q[sz].opt=2;  q[sz].num=i;
        }
        if (s[1]=='D') {
            int x; scanf("%d",&x);
            q[++sz].x=a[x].x1; 
            q[sz].l=a[x].y1; q[sz].r=a[x].y2;
            q[sz].opt=3; q[sz].num=i;
            q[++sz].x=a[x].x2;
            q[sz].l=a[x].y1; q[sz].r=a[x].y2;
            q[sz].opt=4; q[sz].num=i;
        }
        if (s[1]=='Q') {
            int x1,x2,y1,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            Y[++cnty]=y1; Y[++cnty]=y2;
            X[++cntx]=x1; X[++cntx]=x2; X[++cntx]=x1-1;
            q[++sz].x=x1-1; q[sz].l=y1; q[sz].r=y2;
            q[sz].opt=5; q[sz].num=i;
            q[++sz].x=x2; q[sz].l=y1; q[sz].r=y2;
            q[sz].opt=6; q[sz].num=i;
        }
    }
    sort(X+1,X+cntx+1); sort(Y+1,Y+cnty+1);
    cntx=unique(X+1,X+cntx+1)-X-1;
    cnty=unique(Y+1,Y+cnty+1)-Y-1;
    for (int i=1;i<=sz;i++) {
        q[i].x=lower_bound(X+1,X+cntx+1,q[i].x)-X;
        q[i].l=lower_bound(Y+1,Y+cnty+1,q[i].l)-Y;
        q[i].r=lower_bound(Y+1,Y+cnty+1,q[i].r)-Y;
    }
    cdq(1,sz);
    for (int i=1;i<=sz;i++) 
     if (q[i].opt==5) {
        printf("%d\n",q[i+1].ans-q[i].ans);
     }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值