zoukankan      html  css  js  c++  java
  • APIO2018解题报告

    今年的APIO好邪啊。

    T1铁人两项

    题目大意

    给一个无向图,问有多少三元组(三个元素两两不同)使得它们构成一条简单路径 。

    题解

    无向图这种东西不好直接处理,考虑点双缩点建圆方树。

    然后就出现了一个比较神的做法,把方点点权设为点双大小,圆点点权设为-1,那么我们枚举三元组中的起点和终点,那么这条路径的权值就是可能的中间点个数。

    为什么?考虑两种圆点。

    1、起点和终点,因为它们已经被当成三元组中的元素了,所以我们要在和它们相邻的点双中减掉它们,所以这个减一是对的。

    2、其他圆点,这些圆点会被相邻的两个点双算两次,所以我们要减一。

    发现我们只需算出每个点的贡献和,然后可以算每个点被覆盖了多少次再乘上权值就可以了。

    代码

    #include<iostream>
    #include<cstdio>
    #define N 210002
    using namespace std;
    typedef long long ll;
    ll ans,size[N],val[N],sum;
    int head[N],h[N],st[N],tot,tot1,top,n,m,num,dfn[N],low[N],tott;
    struct edge{
        int n,to;
    }e[N*3],E[N*3];
    inline void add(int u,int v){
        e[++tot].n=head[u];
        e[tot].to=v;
        head[u]=tot;
    }
    inline void add1(int u,int v){
        E[++tot1].n=h[u];
        E[tot1].to=v;
        h[u]=tot1;
    }
    void tarjan(int u,int fa){
        dfn[u]=low[u]=++tott;st[++top]=u;
       val[u]=-1;sum++;
        for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
            int v=e[i].to;
            if(!dfn[v]){
                tarjan(v,u);
                low[u]=min(low[u],low[v]); 
                if(low[v]>=dfn[u]){
                   add1(u,++num);int x=0;val[num]=1;
                    do{
                        x=st[top--];
                        val[num]++;
                        add1(num,x);
                    }
                    while(x!=v);
                }
            }
            else low[u]=min(low[u],dfn[v]);
        }
    }
    void dfs(int u){
        if(u<=n)size[u]=1;
        for(int i=h[u];i;i=E[i].n){
            int v=E[i].to;
            dfs(v);
            ans+=2ll*size[u]*size[v]*val[u];
            size[u]+=size[v];
        }
        ans+=2ll*size[u]*(sum-size[u])*val[u];
    }
    inline int rd(){
        int x=0;char c=getchar();bool f=0;
        while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return f?-x:x;
    }
    int main(){
        n=rd();m=rd();int u,v;num=n;
        for(int i=1;i<=m;++i){u=rd();v=rd();add(u,v);add(v,u);}
        for(int i=1;i<=n;++i)if(!dfn[i]){sum=0;tarjan(i,0);dfs(i);}
        cout<<ans;
        return 0;
    } 
    View Code

    T2选圆圈

    题目大意

    给定n个圆,每次挑出半径最大的圆删除,同时删除和这个圆有交的所有圆,知道所有圆都被删除,求出每个圆是被那个圆删掉的。

    题解

    如果把它看成一道一般的计算几何题,会很麻烦。

    如果会KD-tree的话,就好说了,直接建立KD-tree,每次在上面暴力找就好了。

    树上维护最左和最右,找的时候判一下如果和最左最右都没有交就不往下找了。

    代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #define N 300002
    using namespace std;
    typedef long long ll;
    int n,tot,ans[N];
    inline ll rd(){
        ll x=0;char c=getchar();bool f=0;
        while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return f?-x:x; 
    }
    inline ll sq(ll x){return x*x;} 
    int now_x;
    struct zb{
        ll x[2],r;int id;
        bool operator <(const zb &b)const{
            return x[now_x]<b.x[now_x];
        }
    }a[N];
    inline bool cmp(zb a,zb b){if(a.r!=b.r)return a.r>b.r;else return a.id<b.id;}
    struct node{
        zb x;int ls,rs;ll mx[2],mi[2];
    }tr[N];
    inline void update(int p){
        int ls=tr[p].ls,rs=tr[p].rs;
        for(int i=0;i<2;++i){
            tr[p].mx[i]=tr[p].x.x[i]+tr[p].x.r;
            tr[p].mi[i]=tr[p].x.x[i]-tr[p].x.r;
            if(ls){
                tr[p].mx[i]=max(tr[p].mx[i],tr[ls].mx[i]);
                tr[p].mi[i]=min(tr[p].mi[i],tr[ls].mi[i]);
            }
            if(rs){
                tr[p].mx[i]=max(tr[p].mx[i],tr[rs].mx[i]);
                tr[p].mi[i]=min(tr[p].mi[i],tr[rs].mi[i]); 
            }
        }
    }
    int build(int l,int r,int tag){
        if(l>r)return 0;
        int mid=(l+r)>>1;
        now_x=tag;
        nth_element(a+l,a+mid+1,a+r+1);
        int p=++tot;
        tr[p].x=a[mid];
        tr[p].ls=build(l,mid-1,tag^1);
        tr[p].rs=build(mid+1,r,tag^1);
        update(p); return p;
    }
    inline bool pd(int x,zb y){
        if(tr[x].mx[0]<y.x[0]-y.r)return 0;
        if(tr[x].mx[1]<y.x[1]-y.r)return 0;
        if(tr[x].mi[0]>y.x[0]+y.r)return 0;
        if(tr[x].mi[1]>y.x[1]+y.r)return 0;
        return 1;
    }
    inline bool check(zb x,zb y){
        ll dis1=sq(x.x[0]-y.x[0])+sq(x.x[1]-y.x[1]),dis2=sq(x.r+y.r);
        if(dis2>=dis1)return 1;else return 0;
    }
    void work(int p,zb x){
        if(!ans[tr[p].x.id]&&check(tr[p].x,x))ans[tr[p].x.id]=x.id;
        if(tr[p].ls&&pd(tr[p].ls,x))work(tr[p].ls,x);
        if(tr[p].rs&&pd(tr[p].rs,x))work(tr[p].rs,x);
    }
    int main(){
        n=rd();
        for(int i=1;i<=n;++i)a[i].x[0]=rd(),a[i].x[1]=rd(),a[i].r=rd(),a[i].id=i;
        int root=build(1,n,0);
        sort(a+1,a+n+1,cmp);
        for(int i=1;i<=n;++i)if(!ans[a[i].id]){
            ans[a[i].id]=a[i].id;
            work(root,a[i]);
        }
        for(int i=1;i<=n;++i)printf("%d ",ans[i]);
        return 0;
    } 
    View Code

    T3新家

    题目大意

    给定n个四元组,表示每个商店的位置,类型,开门时间和关门时间,有若干次询问,每次问一个时间点上的一个位置的代价,代价指所有类型到这个点的最小距离的最大值。

    上课时讲了一个NB的线段树分治的做法,但我现在仍不会。

    这道题虽并不简单,但仍然有很多套路的地方在里面 。

    题解

    首先,一个区间我们把它拆成两个,在l和r+1处,然后把他们和询问放在一起按时间排序,这样我们就可以忽略时间的影响了。

    然后我们考虑这样一个问题,给出一个数轴,里面有各种类型的点,给定询问点,求一个最小半径,使得半径以内有所有类型的点。

    不难想到二分答案,然后直接做非常麻烦,考虑维护一个pre,记录上一个改类型的点出现的位置,这玩意我们可以用multiset维护。

    当我们二分的答案mid合法时,需满足mid-pos<=pos-min(mid+1,n)后面的min是指后面区域内所有的pre,这个比较显然。

    然后我们用线段树维护所有位置的min,每个节点用一个可删堆去维护在该位置的所有pre。

    最好不要(或不能)离散化,要讨论好多种情况,直接动态开点就可以了。

    优化

    直接做是两个log的,瓶颈在查询上,去膜了kczno1的题解。

    由于我们有了线段树,所以可以不用二分答案了,直接在线段树上二分,判一下mid+1是否合法,看一下是往左边走还是往右边走。

    但为什么我的一个log跑的比两个log还慢?

    代码

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<queue> 
      4 #include<set>
      5 #include<algorithm>
      6 #include<cstring> 
      7 #define N 300002
      8 #define inf 2e8
      9 using namespace std;
     10 typedef long long ll;
     11 int tr[N*32],Ls[N*32],Rs[N*32],n,k,qu,tot,ans[N],ji,tong[N],gan,top,wei[N*32],wen,root;
     12 inline int rd(){
     13     int x=0;char c=getchar();bool f=0;
     14     while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
     15     while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
     16     return f?-x:x;
     17 }
     18 struct node{
     19     int tmp,pos,tim,tag;
     20     bool operator <(const node &b)const{
     21         if(tim!=b.tim)return tim<b.tim;
     22         else return tag<b.tag;
     23     }
     24 }q[N<<2];
     25 struct HESP{
     26     priority_queue<int,vector<int>,greater<int> >q1,q2;
     27     inline void push(int x){q1.push(x);}
     28     inline void del(int x){
     29         q2.push(x);while(q2.size()&&q1.top()==q2.top())q1.pop(),q2.pop();
     30     }
     31     inline int top(){return q1.top();}
     32     inline int size(){return q1.size()-q2.size();}
     33 }h[N*32];
     34 multiset<int>s[N];
     35 multiset<int>::iterator it,it2,it3;
     36 void upd(int &cnt,ll l,ll r,int x,int x1,int x2){
     37     if(!cnt)cnt=++top; 
     38 //    cout<<cnt<<" "<<l<<" "<<r<<endl;
     39     if(l==r){
     40     //    cout<<l<<" ";
     41         if(!wei[cnt])wei[cnt]=++wen;
     42         if(~x1)h[wei[cnt]].push(x1);
     43         if(~x2)h[wei[cnt]].del(x2);
     44         if(h[wei[cnt]].size())tr[cnt]=h[wei[cnt]].top();else tr[cnt]=inf;
     45 //        cout<<r<<endl; 
     46         return;
     47     }
     48     int mid=(l+r)>>1;
     49     if(mid>=x)upd(Ls[cnt],l,mid,x,x1,x2);
     50     else upd(Rs[cnt],mid+1,r,x,x1,x2);
     51     tr[cnt]=min(tr[Ls[cnt]],tr[Rs[cnt]]); 
     52 }
     53 inline int query(int pos){
     54     int cnt=1,l=1,r=top,now=inf;
     55     while(l<r){
     56         int mid=(l+r)>>1;int tmp=min(now,tr[cnt<<1|1]);
     57         if(pos<=mid&&tmp+mid>=2*pos)cnt=Ls[cnt],r=mid,now=tmp;
     58         else cnt=Rs[cnt],l=mid+1; 
     59     }
     60     return l-pos;
     61 }
     62 int check(int cnt,ll l,ll r,int L,int R){
     63     if(!cnt)return inf;
     64     if(l>=L&&r<=R)return tr[cnt];
     65     int mid=(l+r)>>1,ans=2e9;
     66     if(mid>=L)ans=min(ans,check(Ls[cnt],l,mid,L,R));
     67     if(mid<R)ans=min(ans,check(Rs[cnt],mid+1,r,L,R));
     68     return ans;
     69 }
     70 int main(){
     71     n=rd();k=rd();qu=rd();int x,t,a,b;
     72     memset(tr,0x7f,sizeof(tr));
     73     for(int i=1;i<=n;++i){
     74         x=rd();t=rd();a=rd();b=rd();
     75         q[++tot]=node{t,x,a,1};
     76         q[++tot]=node{t,x,b+1,-1};
     77     }
     78     for(int i=1;i<=qu;++i){
     79        q[++tot].pos=rd(),q[tot].tim=rd(),q[tot].tmp=i;
     80        q[tot].tag=inf;
     81     }
     82     sort(q+1,q+tot+1);
     83     for(int i=1;i<=k;++i){
     84         s[i].insert(inf);s[i].insert(-inf);
     85         upd(root,1,inf,inf,-inf,-1);
     86     }
     87     sort(q+1,q+tot+1);
     88     for(int i=1;i<=tot;++i){
     89         int pos=q[i].pos;    
     90         if(q[i].tag==1){
     91             if(!tong[q[i].tmp])ji++;tong[q[i].tmp]++;
     92             it=s[q[i].tmp].upper_bound(pos);it2=it--;
     93             upd(root,1,inf,*it2,pos,*it);
     94             upd(root,1,inf,pos,*it,-1);
     95             s[q[i].tmp].insert(pos); 
     96         }
     97         else if(q[i].tag==-1){
     98             tong[q[i].tmp]--;if(!tong[q[i].tmp])ji--;
     99             it=s[q[i].tmp].find(pos);
    100             it2=it3=it;it2--;it3++;
    101             upd(root,1,inf,*it3,*it2,pos); 
    102             upd(root,1,inf,*it,-1,*it2);
    103             s[q[i].tmp].erase(it);
    104         }
    105         else{
    106             if(ji!=k){ans[q[i].tmp]=-1;continue;}
    107             int l=pos,r=inf,as=-1;
    108             while(l<=r){
    109                 int mid=(l+r)>>1;
    110                 if(pos-check(1,1,inf,mid+1,inf)<=mid-pos){     
    111                     r=mid-1;as=mid;
    112                 }else l=mid+1; 
    113             }
    114             ans[q[i].tmp]=as-pos;
    115         //    ans[q[i].tmp]=query(pos);
    116         }
    117     }
    118     for(int i=1;i<=qu;++i)printf("%d
    ",ans[i]); 
    119     return 0;
    120 }
    View Code
  • 相关阅读:
    Java IO 之 RandomAccessFile 操作文件内容
    Java IO 之 FileFilter与FilenameFilter
    Java IO 之 File 的创建、重命名与遍历
    Java IO 之 System类
    JVM内存区域配置
    切换 显示与隐藏页面元素
    thead tfoot tbody标签的使用
    使用锚点在HTML页面中快速移动
    ASP.NET Core 项目归档
    检测到有潜在危险的 Request.Form 值 转
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10142415.html
Copyright © 2011-2022 走看看