BZOJ3205:[Apio2013]机器人 斯坦纳树+单位边权多源最短路

思路:

就是一堆机器人合并起来,看起来天然的生成树模型吗.

令\(f[i][j][x][y]\)表示区间\([i,j]\)的机器人合并到位置\((x,y)\)使得最小推动次数,模仿斯坦纳树,我们有以下两种转移:

\[f[i][j][x][y]=f[i][j][x'][y']+1\]

\[f[i][j][x][y]=f[i][k][x][y]+f[k+1][j][x][y]\]

第二种转移直接暴力即可.

对于第一种转移,我们则可以利用一次最短路spfa求解.

至于如何构图呢?我们需要预处理出对于机器人目前在的每一个位置,我们将机器人推动一个方向后最终处于的位置.

我煞笔被坑了几次:首先机器人最终是可以停在转动器位置的.

如果我们每次暴力枚举,会T,因此只能利用记忆化搜索来做.

反正这个预处理坑了我好久.(太弱不解释

 

可是只有这些优化还是不能AC的!

我们每次暴力spfa的耗时还是过长了.

这怎么优化?

我们发现边权都是1,下面介绍一种神奇的方法:

我们维护两个队列,首先将所有的点按照距离从小到大的顺序加入第二个队列中,随后进行松弛,每次选择两个队列队头距离比较小的点取出进行松弛,并将松弛后的点加入第一个队列中,这样就起到了优先队列的作用,且不用带一个\(log\),这样做是线性的.

这样才能AC!

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
 
#define INF 0x1f1f1f1f
 
#define N 10
#define W 510
#define H 510
int n,w,h;
char s[W][H];
     
int f[N][N][W][H],tclock,vis[W][H][4];
 
int nowi,nowj;
struct Ins{
    short x,y;
    Ins(){}
    Ins(int _x,int _y):x(_x),y(_y){}
    bool operator<(const Ins&B)const{return f[nowi][nowj][x][y]<f[nowi][nowj][B.x][B.y];}
}vec[W][H][4];
 
const int dx[]={-1,0,1,0},dy[]={0,-1,0,1};
 
queue<Ins>q1,q2;
 
Ins seq[W*H],tmp[W*H];int siz,rk[W*H],t[200010],cnt[200010];
inline void add(int x,int c,int v){if(t[x]!=v)t[x]=v,cnt[x]=0;cnt[x]+=c;}
inline int ask(int x,int v){if(t[x]!=v)t[x]=v,cnt[x]=0;return cnt[x];}
 
int sig;
inline void Rdxsort(){
    ++sig;
    int Max=0;register int i;int x,y;
    for(i=1;i<=siz;++i)add(f[nowi][nowj][x=seq[i].x][y=seq[i].y],1,sig),Max=max(Max,f[nowi][nowj][x][y]);
    for(i=1;i<=Max;++i)add(i,ask(i-1,sig),sig);
    for(i=1;i<=siz;++i)rk[i]=ask(f[nowi][nowj][x=seq[i].x][y=seq[i].y],sig),add(f[nowi][nowj][x][y],-1,sig),tmp[rk[i]]=seq[i];
    for(i=1;i<=siz;++i)seq[i]=tmp[i];
}
 
bool inq[W][H];
inline void work(){
    register int i,j,k;
    for(i=1;i<=siz;++i)q2.push(seq[i]),inq[seq[i].x][seq[i].y]=1;
    Ins t,to;int d1,d2;
    while(!q1.empty()||!q2.empty()){
        if(q1.empty())t=q2.front(),q2.pop();
        else if(q2.empty())t=q1.front(),q1.pop();
        else{
            d1=f[nowi][nowj][q1.front().x][q1.front().y];
            d2=f[nowi][nowj][q2.front().x][q2.front().y];
            if(d1<d2)t=q1.front(),q1.pop();else t=q2.front(),q2.pop();
        }
        for(inq[t.x][t.y]=k=0;k<4;++k){
            if((to=vec[t.x][t.y][k]).x!=INF&&f[nowi][nowj][to.x][to.y]>f[nowi][nowj][t.x][t.y]+1){
                f[nowi][nowj][to.x][to.y]=f[nowi][nowj][t.x][t.y]+1;
                if(!inq[to.x][to.y])inq[to.x][to.y]=1,q1.push(to);
            }
        }
    }
}
 
Ins calc(int x,int y,int v){
    if(vis[x][y][v]==tclock)return Ins(-1,-1);vis[x][y][v]=tclock;
    if(!(vec[x][y][v].x==0&&vec[x][y][v].y==0))return vec[x][y][v];
    int nowv=v;if(s[x][y]=='A')nowv=(v+1)%4;if(s[x][y]=='C')nowv=(v+3)%4;
    int tx=x+dx[nowv],ty=y+dy[nowv];
    if(s[tx][ty]=='x')return vec[x][y][v]=Ins(x,y);
    return vec[x][y][v]=calc(tx,ty,nowv);
}
 
int main(){
    scanf("%d%d%d",&n,&h,&w);
    register int i,j,k,p,q;
    for(i=1;i<=w;++i)scanf("%s",s[i]+1);
    memset(f,0x1f,sizeof f);
    int id;
    for(i=1;i<=w;++i)for(j=1;j<=h;++j)if(s[i][j]>='1'&&s[i][j]<='9')id=s[i][j]-'0',f[id][id][i][j]=0;
 
    for(i=0;i<=w+1;++i)s[i][0]=s[i][h+1]='x';
    for(j=0;j<=h+1;++j)s[0][j]=s[w+1][j]='x';
 
    for(i=1;i<=w;++i)for(j=1;j<=h;++j)if(s[i][j]!='x')
        for(k=0;k<4;++k)
            ++tclock,vec[i][j][k]=calc(i,j,k);
 
    for(int l=1;l<=n;++l)for(i=1;i+l-1<=n;++i){
        j=i+l-1;
        for(k=i;k<j;++k)for(p=1;p<=w;++p)for(q=1;q<=h;++q)f[i][j][p][q]=min(f[i][j][p][q],f[i][k][p][q]+f[k+1][j][p][q]);
        siz=0;for(p=1;p<=w;++p)for(q=1;q<=h;++q)if(f[i][j][p][q]!=INF)seq[++siz]=Ins(p,q);
        nowi=i,nowj=j,Rdxsort();
        work();
    }
 
    int ans=INF;
    for(i=1;i<=w;++i)for(j=1;j<=h;++j)ans=min(ans,f[1][n][i][j]);
 
    if(ans==INF)puts("-1");else printf("%d\n",ans);
 
    return 0;
}

BZOJ2595: [Wc2008]游览计划 斯坦纳树

思路:

斯坦纳树裸题.

输出方案的话记录一下转移就好了.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
 
#define INF 0x1f1f1f1f
 
#define N 11
#define M 11
int n,m,d[N][M],lab[N][M],cnt;
 
int f[N][M][1<<11];
 
struct Lastans{
    int x,y,S;
    Lastans(){}
    Lastans(int _x,int _y,int _S):x(_x),y(_y),S(_S){}
}g[N][M][1<<11];
 
struct Ins{
    int x,y;
    Ins(){}
    Ins(int _x,int _y):x(_x),y(_y){}
};
queue<Ins>q;
 
const int dx[]={-1,1,0,0},dy[]={0,0,1,-1};
bool inq[N][M];
#define Inrange(x,l,r) ((x)>=(l)&&(x)<=(r))
inline void spfa(int S){
    int tx,ty,i,j;
    while(!q.empty()){
        Ins t=q.front();q.pop();inq[i=t.x][j=t.y]=0;
        for(int k=0;k<4;++k)if(Inrange(tx=i+dx[k],1,n)&&Inrange(ty=j+dy[k],1,m)){
            if(f[tx][ty][S]>f[i][j][S]+d[tx][ty]){
                f[tx][ty][S]=f[i][j][S]+d[tx][ty],g[tx][ty][S]=Lastans(i,j,S);
                if(!inq[tx][ty])inq[tx][ty]=1,q.push(Ins(tx,ty));
            }
        }
    }
}
 
bool vis[N][M];
inline void dfs(int x,int y,int S){
    if(!S)return;
    vis[x][y]=1;if((!d[x][y])&&(S==(1<<lab[x][y])))return;
    Lastans p=g[x][y][S];
    dfs(p.x,p.y,p.S);
    if(S!=p.S)dfs(x,y,S-p.S);
}
 
int main(){
    scanf("%d%d",&n,&m);
    register int i,j;for(i=1;i<=n;++i)for(j=1;j<=m;++j){scanf("%d",&d[i][j]);if(!d[i][j])lab[i][j]=cnt++;}
    memset(f,0x1f,sizeof f);
    for(i=1;i<=n;++i)for(j=1;j<=m;++j)if(!d[i][j])f[i][j][1<<lab[i][j]]=0;
    for(int S=1;S<(1<<cnt);++S){
        for(int mas=(S-1)&S;mas;mas=(mas-1)&S)
            for(i=1;i<=n;++i)
                for(j=1;j<=m;++j)
                    if(f[i][j][S]>f[i][j][mas]+f[i][j][S-mas]-d[i][j])
                        f[i][j][S]=f[i][j][mas]+f[i][j][S-mas]-d[i][j],g[i][j][S]=Lastans(i,j,mas);
        memset(inq,0,sizeof inq);
        for(i=1;i<=n;++i)for(j=1;j<=m;++j)if(f[i][j][S]!=INF)q.push(Ins(i,j)),inq[i][j]=1;
        spfa(S);
    }
 
    Lastans res;
    int ans=INF;for(i=1;i<=n;++i)for(j=1;j<=m;++j)if(ans>f[i][j][(1<<cnt)-1])ans=f[i][j][(1<<cnt)-1],res=Lastans(i,j,(1<<cnt)-1);
    printf("%d\n",ans);
    dfs(res.x,res.y,(1<<cnt)-1);
    for(i=1;i<=n;++i){
        for(j=1;j<=m;++j)if(!vis[i][j])putchar('_');else if(vis[i][j]&&d[i][j]==0)putchar('x');else putchar('o');puts("");
    }
    return 0;
}

BZOJ3611: [Heoi2014]大工程 LCA单调性+树形dp

思路:思路基本同上一道题.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
 
#define INF 0x1f1f1f1f
 
namespace Fio{
    inline int getc(){
        static const int L=1<<15;static char buf[L],*S=buf,*T=buf;
        if(S==T){T=(S=buf)+fread(buf,1,L,stdin);if(S==T)return EOF;}
        return*S++;
    }
    template<typename T>inline void Get(T&x){
        int c;while(!isdigit(c=getc()));x=c-'0';while(isdigit(c=getc()))x=(x<<1)+(x<<3)+c-'0';
    }
}
#define N 1000010
int Head[N],Next[N<<1],End[N<<1],Len[N<<1];
inline void addedge(int a,int b,int x){static int q=1;End[q]=b,Next[q]=Head[a],Head[a]=q,Len[q++]=x;}
inline void make(int a,int b,int x){addedge(a,b,x),addedge(b,a,x);}
 
int dfn[N],id,dep[N],pa[N][21];
inline bool cmp(const int&x,const int&y){return dfn[x]<dfn[y];}
inline void dfs(int x,int fa){
    dfn[x]=++id;
    for(int j=Head[x];j;j=Next[j])if(End[j]!=fa)dep[End[j]]=dep[x]+1,pa[End[j]][0]=x,dfs(End[j],x);
}
int lca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;--i)if(dep[pa[x][i]]>=dep[y])x=pa[x][i];
    if(x==y)return x;
    for(int i=20;i>=0;--i)if(pa[x][i]!=pa[y][i])x=pa[x][i],y=pa[y][i];
    return pa[x][0];
}
int getdis(int son,int Anc){
    //int r=0;for(int i=20;i>=0;--i)if(dep[pa[son][i]]>=dep[Anc])r+=w[son][i],son=pa[son][i];return r;
    return dep[son]-dep[Anc];
}
 
struct Array{
    int d[N],t[N],init;
    Array(){}
    Array(int _init):init(_init){}
    inline int ask(int x,int nowv){
        if(t[x]!=nowv)t[x]=nowv,d[x]=init;return d[x];
    }
    inline void modify(int x,int to,int nowv){
        t[x]=nowv,d[x]=to;
    }
    inline void maxi(int x,int to,int nowv){
        if(t[x]!=nowv)t[x]=nowv,d[x]=init;if(d[x]<to)d[x]=to;
    }
    inline void mini(int x,int to,int nowv){
        if(t[x]!=nowv)t[x]=nowv,d[x]=init;if(d[x]>to)d[x]=to;
    }
    inline void add(int x,int c,int nowv){
        if(t[x]!=nowv)t[x]=nowv,d[x]=init;d[x]+=c;
    }
};
 
int nowv;
struct Tree{
    Array h;int next[N],end[N],len[N],ind;
    inline void reset(){h.init=-1;ind=0;}
    inline void addedge(int a,int b){int q=ind++;end[q]=b,next[q]=h.ask(a,nowv),h.modify(a,q,nowv),len[q++]=getdis(b,a);}
}G;
 
int seq[N],siz,end;
 
int stk[N],top,Anc[N];
 
Array isimp(0),maxdep(-INF),mindep(INF),cnt(0);
long long tot;
int maxans,minans;
inline void mini(int&x,int y){if(x>y)x=y;}
inline void maxi(int&x,int y){if(x<y)x=y;}
void Solve(int x){
    for(int j=G.h.ask(x,nowv);j!=-1;j=G.next[j])Solve(G.end[j]),cnt.add(x,cnt.ask(G.end[j],nowv),nowv),tot+=(long long)G.len[j]*cnt.ask(G.end[j],nowv)*(siz-cnt.ask(G.end[j],nowv));
    if(isimp.ask(x,nowv))maxdep.modify(x,0,nowv),mindep.modify(x,0,nowv),cnt.add(x,1,nowv);
    for(int j=G.h.ask(x,nowv);j!=-1;j=G.next[j])
        mini(minans,mindep.ask(x,nowv)+mindep.ask(G.end[j],nowv)+G.len[j]),
        mindep.mini(x,mindep.ask(G.end[j],nowv)+G.len[j],nowv);
    for(int j=G.h.ask(x,nowv);j!=-1;j=G.next[j])
        maxi(maxans,maxdep.ask(x,nowv)+maxdep.ask(G.end[j],nowv)+G.len[j]),
        maxdep.maxi(x,maxdep.ask(G.end[j],nowv)+G.len[j],nowv);
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("tt.in","r",stdin);
    //freopen("tt.out","w",stdout);
    #endif
    using namespace Fio;
    int n;Get(n);
    register int i,j;int a,b;for(i=1;i<n;++i)Get(a),Get(b),make(a,b,1);
    dep[1]=1,dfs(1,-1);
    for(j=1;j<=20;++j)
        for(i=1;i<=n;++i)pa[i][j]=pa[pa[i][j-1]][j-1];
    int Q;Get(Q);
    while(Q--){
        Get(siz);for(i=1;i<=siz;++i)Get(seq[i]);end=siz;sort(seq+1,seq+siz+1,cmp);
        ++nowv;for(i=1;i<=siz;++i)isimp.modify(seq[i],1,nowv);
        for(top=0,i=1;i<=siz;++i){
            if(!top)stk[++top]=seq[i];
            else{
                int Lca=lca(stk[top],seq[i]);Anc[seq[i]]=Lca;
                while(dep[stk[top]]>dep[Lca]){if(dep[stk[top-1]]<=dep[Lca])Anc[stk[top]]=Lca;--top;}
                if(Lca!=stk[top])Anc[Lca]=stk[top],seq[++end]=stk[++top]=Lca;
                stk[++top]=seq[i];
            }
        }
        sort(seq+1,seq+end+1,cmp);for(G.reset(),i=2;i<=end;++i)G.addedge(Anc[seq[i]],seq[i]);
        tot=0,minans=INF,maxans=-INF,Solve(seq[1]);
        printf("%lld %d %d\n",tot,minans,maxans);
    }
    return 0;
}

BZOJ2286: [Sdoi2011]消耗战 LCA单调性+树形dp

思路:

首先对于一次询问进行一次朴素的树形dp极为容易.不过这样需要\(O(n)\)的时间复杂度,这样总的时间复杂度为\(O(qn)\),无法通过.

我们能否对于一次询问,将时间复杂度限定至仅与关键点数有关呢?

 

我们考虑构造一个树结构,仅仅将关键点连接起来,并适当添加部分非关键点,我们如果在这样减缩后的树上做dp,就能使得复杂度只与关键点数有关.

我们利用一个单调栈维护树上的一条深度单调递增的链,当我们要添加的点与栈顶的点的lca深度小于栈顶时,我们不断弹栈并在这个过程中顺便维护此时树的形态.

容易发现,对于每次添加,至多在新树上添加一个点,因此新树的点数只与关键点线性相关.

 

接下来在新树上作朴素的树形dp就行了.

 

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
namespace Fio{
    inline int getc(){
        static const int L=1<<15;static char buf[L],*S=buf,*T=buf;
        if(S==T){T=(S=buf)+fread(buf,1,L,stdin);if(S==T)return EOF;}
        return*S++;
    }
    template<typename T>inline void Get(T&x){
        int c;while(!isdigit(c=getc())&&c!='-');bool sign=c=='-';
        x=sign?0:c-'0';while(isdigit(c=getc()))x=(x<<1)+(x<<3)+c-'0';
        if(sign)x=-x;
    }
    char buf[5000010],*o=buf;
    template<typename T>inline void print(T x){
        static int stk[100];int top=0;if(x<0)*o++='-',x=-x;
        if(!x)*o++=48;else{for(;x;x/=10)stk[++top]=x%10;for(int i=top;i>=1;--i)*o++=48+stk[i];}
        *o++='\n';
    }
    inline void Final(){fwrite(buf,1,o-buf,stdout);}
}
 
#define N 250010
int head[N],next[N<<1],end[N<<1],len[N<<1];
inline void addedge(int a,int b,int x){
    static int q=1;end[q]=b,next[q]=head[a],len[q]=x,head[a]=q++;
}
inline void make(int a,int b,int x){addedge(a,b,x),addedge(b,a,x);}
 
int pa[N][21],w[N][21],dfn[N],id,dep[N];
void dfs(int x,int fa){
    dfn[x]=++id;
    for(int j=head[x];j;j=next[j])if(end[j]!=fa){
        dep[end[j]]=dep[x]+1,pa[end[j]][0]=x,w[end[j]][0]=len[j],dfs(end[j],x);
    }
}
int lca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;--i)if(dep[pa[x][i]]>=dep[y])x=pa[x][i];
    if(x==y)return x;
    for(int i=20;i>=0;--i)if(pa[x][i]!=pa[y][i])x=pa[x][i],y=pa[y][i];
    return pa[x][0];
}
long long calc(int son,int Anc){
    int res=100010;for(int i=20;i>=0;--i)if(dep[pa[son][i]]>=dep[Anc])res=min(res,w[son][i]),son=pa[son][i];return res;
}
 
int seq[N],siz;
 
inline bool cmp(const int&a,const int&b){return dfn[a]<dfn[b];}
 
int stk[N],top,Anc[N];
 
struct Array{
    long long sum[N];int t[N];
    inline void modify(int x,int add,int v){
        if(t[x]!=v)t[x]=v,sum[x]=0;sum[x]+=add;
    }
    inline long long ask(int x,int v){
        if(t[x]!=v)t[x]=v,sum[x]=0;return sum[x];
    }
}f,IsImp;int nowv;
 
int main(){
    #ifndef ONLINE_JUDGE
    freopen("tt.in","r",stdin);
    #endif
    using namespace Fio;
    int n;Fio::Get(n);
    register int i,j;int a,b,x;for(i=1;i<n;++i)Get(a),Get(b),Get(x),make(a,b,x);
    dep[1]=1,dfs(1,-1);
    for(j=1;j<=20;++j)
        for(i=1;i<=n;++i)pa[i][j]=pa[pa[i][j-1]][j-1],w[i][j]=min(w[i][j-1],w[pa[i][j-1]][j-1]);
    int Q;Get(Q);
    while(Q--){
        Get(siz);for(i=1;i<=siz;++i)Get(seq[i]);++nowv;for(i=1;i<=siz;++i)IsImp.modify(seq[i],1,nowv);seq[++siz]=1;
        sort(seq+1,seq+siz+1,cmp);int end=siz;
        for(top=0,i=1;i<=siz;++i){
            if(!top)stk[++top]=seq[i];
            else{
                int Lca=lca(stk[top],seq[i]);Anc[seq[i]]=Lca;
                while(dep[stk[top]]>dep[Lca]){if(dep[stk[top-1]]<=dep[Lca])Anc[stk[top]]=Lca;--top;}
                if(stk[top]!=Lca)Anc[Lca]=stk[top],seq[++end]=stk[++top]=Lca;
                stk[++top]=seq[i];
            }
        }
        sort(seq+1,seq+end+1,cmp);
        //for(i=1;i<=siz;++i)printf("%d ",seq[i]);puts("");
        long long dis;
        for(i=end;i>=1;--i){
            dis=calc(seq[i],Anc[seq[i]]);
            if(IsImp.ask(seq[i],nowv))f.modify(Anc[seq[i]],dis,nowv);else f.modify(Anc[seq[i]],min(dis,f.ask(seq[i],nowv)),nowv);
        }
        print(f.ask(1,nowv));
    }
    return Final(),0;
}


BZOJ2961:共点圆 圆的反演+cdq分治

思路:

(现在我还没有AC这道题,先记录一下思路吧T^T)

昨天膜拜了xhr犇的论文,get了一系列新姿势.

总的来说,保证修改相互独立的数据结构问题可以用如下三种方法来水:

1.对时间分治

题目要求:修改独立,允许离线

我们利用对时间分治,将问题转化为先给出若干修改,然后进行若干询问的形式.这样我们就可以先对修改进行预处理,随后回答每个询问,这样就避免了动态修改.

2.二进制分组

题目要求:修改独立,强制在线

我们按照顺序进行操作,同时维护已经完成的修改的二进制分组,当在后面加入一个修改的时候,我们暴力重构为新的分组;当回答询问时,我们在之前的\(O(\log n)\)的分组中分别累加答案.这样,我们就通过一个\(\log\)的代价避免了动态修改.

3.整体二分

这个与这道题无关就先不说了.

 

那么来说这道题.

首先有两种基本思路:

[1]利用圆的反演转化为动态半平面交问题

我们以圆心为反演中心,合适长度为半径进行反演,那么每个圆都被反演成一条不过原点的直线(在圆内区域形成一个半平面).我们对每个询问点也进行反演,不难发现若询问点在所有圆的并集之内,则反演点应该在所有直线的半平面交之内.

那么现在问题转化为:支持两个操作,加入一个半平面,询问一个点在不在之前插入的半平面的半平面交之内.

{1}貌似可以拿平衡树维护,\(O(n\log n)\),我表示跪了.

{2}按照时间分治,在一次分治内,我们处理出前一半操作中插入的半平面的半平面交,随后去判断后一半操作中的询问点是否在这个半平面交中,若不在,则表示这个询问点不在所有的圆中.于是这样我们可以做到\(O(n\log ^2n)\).

(至于如何快速判断一个点在不在一个凸包中,我们可以任取凸包内的一个点,将凸包上的点按照关于这个点的极角序排序,对于询问点,我们二分得到询问点在凸包上的哪两个点之间,随后利用\(O(1)\)判断询问点在不在三角形中.这样我们就做到了\(O(\log n)\)询问.)

{3}利用二进制分组,维护\(O(\log n)\)个分组的半平面交,容易发现重构半平面交的复杂度为\(O(n\log ^2n)\),对于每一个询问的复杂度也为\(\log ^2n\),因此总复杂度为\(O(n\log ^2n)\).

[2]通过等式变换转化为另一个问题

由于题目保证圆通过原点,我们考虑点\((x_0,y_0)\)在圆心为\((ox,oy)\)的圆内部或边界上的条件:

\[(x_0-ox)^2+(y_0-oy)^2\leq{ox^2+oy^2}\]

\[x_0^2+y_0^2\leq{2x_0{ox}+2y_0{oy}}\]

这样相当于是每一个询问是给定一个半平面,然后问之前出现过的所有圆形成的点是否都在半平面之内.并且要支持加点.

{1}利用平衡树做动态凸包,\(O(n\log n)\),同样是给跪了.

{2}按时间分治,对于每一次分治,我们预处理出前一半操作中的点形成的凸包,然后去判断后面一半操作中的询问半平面是否完全包含凸包.

我们没有必要作凸包,只需\(O(n)\)维护上下凸壳即可.对于后面的询问,我们可以直接二分,也可以预先将所有询问排序随后线性扫描.

这样就可以做到\(O(n\log ^2n)\)或是\(O(n\log n)\).

{3}二进制分组,我们将之前的\(n\)次加点操作分为\(O(\log n)\)组,然后对于每一次询问在每一个维护的结构中二分.这个方法支持强制在线,不过时间复杂度为\(O(n\log ^2n)\).

 

我目前写的做法是[1]{2},不过貌似精度死得很惨.反演半径居然会影响答案.

现在用[2]{2}的方法AC了.时间复杂度\(O(n\log n)\).

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
typedef double f2;
inline f2 sqr(const f2&x){return x*x;}
 
#define N 500010
struct Case{
    int qte,lab;f2 x,y;
    bool operator<(const Case&B)const{
        if(qte!=B.qte)return qte<B.qte;
        if(qte==0)return x<B.x||(x==B.x&&y<B.y);
        if(qte==1)return(-x/y)<(-B.x/B.y);
    }
}q[N],ql[N],qr[N],up[N],down[N];
int cntl,cntr,topup,topdown;
 
struct Point{
    f2 x,y;
    Point(){}
    Point(f2 _x,f2 _y):x(_x),y(_y){}
    Point operator-(const Point&B)const{return Point(B.x-x,B.y-y);}
};
f2 cross(const Point&A,const Point&B){return A.x*B.y-A.y*B.x;}
struct Line{
    f2 A,B,C;
};
inline Line Getline(const Case&c){
    Line l;
    l.A=2*c.x,l.B=2*c.y,l.C=sqr(c.x)+sqr(c.y);
    return l;
}
inline bool onleft(const Line&l,const Point&p){
    return l.A*p.x+l.B*p.y>=l.C;
}
bool OK[N];
 
inline void Solve(int l,int r){
    if(l==r)return;
    int mid=(l+r)>>1;
    register int i,j;
    for(cntl=cntr=0,i=l;i<=r;++i)if(q[i].lab<=mid)ql[++cntl]=q[i];else qr[++cntr]=q[i];
    int id=l;for(i=1;i<=cntl;++i)q[id++]=ql[i];for(i=1;i<=cntr;++i)q[id++]=qr[i];
    int cnt=0;for(i=l;i<=mid;++i)cnt+=(q[i].qte==0);
    if(cnt){
        for(topup=topdown=0,i=l;i<=mid&&q[i].qte==0;++i){
            while(topup>1&&(q[i].y-up[topup].y)*(up[topup].x-up[topup-1].x)>=(up[topup].y-up[topup-1].y)*(q[i].x-up[topup].x))--topup;
            up[++topup]=q[i];
            while(topdown>1&&(q[i].y-down[topdown].y)*(down[topdown].x-down[topdown-1].x)<=(down[topdown].y-down[topdown-1].y)*(q[i].x-down[topdown].x))--topdown;
            down[++topdown]=q[i];
        }
        int ins;
        for(ins=mid+1;ins<=r;++ins)if(q[ins].qte==1)break;
        if(ins<=r){
            i=ins;
            for(j=1;j<=topdown;++j){
                for(;i<=r&&(j==topdown||-q[i].x/q[i].y*(down[j+1].x-down[j].x)<=(down[j+1].y-down[j].y));++i)
                    if(!onleft(Getline(q[i]),Point(down[j].x,down[j].y)))OK[q[i].lab]=0;   
            }
            i=r;
            for(j=1;j<=topup;++j){
                for(;i>=ins&&(j==topup||-q[i].x/q[i].y*(up[j+1].x-up[j].x)>=up[j+1].y-up[j].y);--i)
                    if(!onleft(Getline(q[i]),Point(up[j].x,up[j].y)))OK[q[i].lab]=0;
            }
        }
    }
    Solve(l,mid),Solve(mid+1,r);
}
 
int main(){
    int n;scanf("%d",&n);
    register int i;for(i=1;i<=n;++i)scanf("%d%lf%lf",&q[i].qte,&q[i].x,&q[i].y),q[i].lab=i;
    for(i=1;i<=n;++i)if(q[i].qte==1)OK[i]=1;
    for(i=1;i<=n&&q[i].qte==1;++i)OK[i]=0;
    sort(q+1,q+n+1);
    Solve(1,n);
    for(i=1;i<=n;++i)if(q[i].qte==1)puts(OK[i]?"Yes":"No");
    return 0;
}

(另外此题真的非常卡精度T^T)

BZOJ3612:[Heoi2014]平衡 整数划分

思路:

首先有两种情况,就是在中轴线处放不放,于是问题转化为在两侧分别放一些橡皮,使得距离之和相同.

我们令\(f[i][j]\)表示在一侧放橡皮,距离之和为\(i\),共放了\(j\)个橡皮的可行方案数.

不难转化为整数分解问题,也就是将整数\(i\)分解为\(j\)个不相同正整数,且每个正整数均不超过\(n\)的问题.

我们依旧分情况讨论:

[1]若划分的最小正整数为1,我们拿走最下面一行,转化为\(f[i-j][j-1]\).

[2]否则,我们拿走最下面一行转化为\(f[i-j][j]\).

不过最大的正整数不能超过\(n\),我们发现我们每次都是在最下面加上一行,那么如果加上后不合法,最后一列就应该是\(n+1\),于是不合法的方案数就应该是\(f[i-n-1][j-1]\).

于是\(f[i][j]=f[i-j][j]+f[i-j][j-1]-f[i-n-1][j-1]\)

记忆化搜索水过.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
int n,k,p,lim;
 
int f[100010][11];
 
int Solve(int i,int j){
    if(j==1)return(i>0&&i<=n)?1:0;
    if(i<=0||i<(1+j)*j/2||i>=j*n)return 0;
    if(f[i][j]!=-1)return f[i][j];
    return f[i][j]=((long long)Solve(i-j,j-1)+Solve(i-j,j)-Solve(i-n-1,j-1)+p)%p;
}
int main(){
    int T;cin>>T;
    register int i,j;
    while(T--){
        scanf("%d%d%d",&n,&k,&p);
        if(k==1){puts("1");continue;}
        memset(f,-1,sizeof f);
        for(i=0;i<=n*k;++i)f[i][0]=0;
        for(i=1;i<=n*k;++i)for(j=1;j<=k;++j)f[i][j]=Solve(i,j);//printf("n=%d m=%d t=%d\n",i,j,f[i][j]);
        long long res=0;
        for(i=1;i<=n*k;++i){
            for(j=1;j<k;++j){
                res=(res+(long long)f[i][j]*f[i][k-j])%p;
                res=(res+(long long)f[i][j]*f[i][k-j-1])%p;
            }
        }
        printf("%d\n",res);
    }return 0;
}

BZOJ3831:[Poi2014]Little Bird dp+单调队列

思路:

一开始是用的线段树优化dp,具体的就不说了.

我一开始觉得有的需要+1,有的不需要加,单调队列没办法做.

不过有一条性质,就是如果两条决策,其中一个的步数已经比另一个决策少,那么我们选择这个决策是一定不劣于另一个决策的.

否则若步数相同,我们当然选择保留高度较高的那个决策.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;

namespace Fio{
    inline int getc(){
        static const int L=1<<15;static char buf[L],*S=buf,*T=buf;
        if(S==T){T=(S=buf)+fread(buf,1,L,stdin);if(S==T)return EOF;}
        return*S++;
    }
    template<typename T>inline void Get(T&x){
        int c;while(!isdigit(c=getc())&&c!='-');bool sig=c=='-';
        x=sig?0:c-'0';while(isdigit(c=getc()))x=(x<<1)+(x<<3)+c-'0';
        if(sig)x=-x;
    }
}
#define N 1000010
int h[N];
int q[N],fr,ta,f[N];
int main(){
	int n;Fio::Get(n);register int i,j;for(i=1;i<=n;++i)Fio::Get(h[i]);
	int Q;Fio::Get(Q);int lim;
	while(Q--){
		 Fio::Get(lim);
		 fr=ta=0;q[ta++]=1;f[1]=0;
		 for(i=2;i<=n;++i){
		 	while(fr^ta&&i>q[fr]+lim)++fr;f[i]=f[q[fr]]+(h[q[fr]]<=h[i]);
		 	while(fr^ta&&((f[i]<f[q[ta-1]])||(f[i]==f[q[ta-1]]&&h[i]>h[q[ta-1]])))--ta;
		 	q[ta++]=i;
		 }
		 printf("%d\n",f[n]);
	}return 0;
}

BZOJ3834:[Poi2014]Solar Panels 分块

思路:

首先若\(x\)可以作为答案,必定满足\(b/i-(a-1)/i\geq{1},d/i-(c-1)/i\geq{1}\).

注意到单独的\(v/i\)是只有\(O(\sqrt{n})\)块的,我们可以单独算出对于\(a,b\)满足条件的块,以及对于\(c,d\)满足条件的块.

接下来我们只需求出 两部分的最大交点即可,我们可以在\(O(\sqrt{n})\)的时间内线性扫描得到.

#include<cstdio>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
 
int l[2][100000],r[2][100000],blks[2];
int main(){
    int T;cin>>T;int a,b,c,d,last,ans;register int i,j,k;
    while(T--){
        scanf("%d%d%d%d",&a,&b,&c,&d);blks[0]=blks[1]=0;
        for(i=1;i<a;i=last+1){
            last=min(b/(b/i),(a-1)/((a-1)/i));
            if(b/i-(a-1)/i>=1)l[0][++blks[0]]=i,r[0][blks[0]]=last;
        }
        l[0][++blks[0]]=a,r[0][blks[0]]=b;
        for(i=1;i<c;i=last+1){
            last=min(d/(d/i),(c-1)/((c-1)/i));
            if(d/i-(c-1)/i>=1)l[1][++blks[1]]=i,r[1][blks[1]]=last;
        }
        l[1][++blks[1]]=c,r[1][blks[1]]=d,
        j=1;
        for(ans=0,i=1;i<=blks[0];++i){
            while(j<=blks[1]&&r[1][j]<l[0][i])++j;
            for(k=j;k<=blks[1]&&l[1][k]<=r[0][i];++k)ans=max(ans,min(r[0][i],r[1][k]));
        }
        printf("%d\n",ans);
    }return 0;
}

BZOJ2476:战场的数目 矩阵乘法+整数划分

思路:

首先注意到不允许出现矩形,而这个条件很难限制,不过我们可以将矩形也一起算进去,在最后减掉,因为矩形的数目是可以\(O(1)\)计算的.

接下来就是考虑令\(f_i\)表示周长为\(i\)的合法方案数.

我们考虑几种情况:

[1]左侧有一个高度为1的方块

[2]右侧有一个高度为1的方块

[3]左右两侧均没有一个高度为1的方块

对于[1][2],我们可以分别在左侧和右侧拿走一个方块使得周长减少2,对于[3],我们可以拿走下面的一层使得周长减少2.不难发现这些关系都是一一对应的.

下面考虑一个问题,如果一种形态是两侧均有一个方块,那么我们事实上是算了两遍的.所以我们需要减去这种情况.而显然这种情况与周长减少了4的情况一一对应.

因此我们有:\(f_i=3f_{i-2}-f_{i-4}\)

利用矩阵乘法即可.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
 
typedef long long LL;
static const int mod=987654321;
struct Matrix{
    int w,h;LL d[2][2];
    Matrix(int _w,int _h):w(_w),h(_h){memset(d,0,sizeof d);}
    inline void operator*=(const Matrix&B){
        Matrix C(w,B.h);
        register int i,j,k;
        for(i=0;i<C.w;++i)
            for(j=0;j<C.h;++j)
                for(k=0;k<h;++k)
                    C.d[i][j]=(C.d[i][j]+d[i][k]*B.d[k][j])%mod;
        *this=C;
    }
};
 
int main(){
    int n;
    while(scanf("%d",&n)&&n){
        if(n<=3||n&1){puts("0");continue;}
        int t=n/2-1,res;
        if(t==1)res=1;else if(t==2)res=2;
        else{
            Matrix ans(1,2);ans.d[0][0]=1,ans.d[0][1]=2;
            Matrix add(2,2);add.d[0][0]=0,add.d[0][1]=-1,add.d[1][0]=1,add.d[1][1]=3;
            Matrix one(2,2);one.d[0][0]=one.d[1][1]=1;
            t-=2;for(;t;t>>=1,add*=add)if(t&1)one*=add;
            ans*=one;
            res=ans.d[0][1];
        }
        res=((LL)res-(n/2-1)+mod)%mod;
        printf("%d\n",res);
    }return 0;
}

BZOJ2508:简单题 拉格朗日乘数法

思路:考虑若给定一个定点\((x_0,y_0)\),以及某条直线\(ax+by+c=0\),那么距离的平方为:

\[\frac{(ax_0+by_0+c)^2}{a^2+b^2}\]

那么对于若干条直线,我们总能将距离的平方和表示为如下的式子:

\[Ax_0^2+By_0^2+Cx_0y_0+Dx_0+Ey_0+F\]

这是一个无限制求函数最值的问题,我们利用拉格朗日乘数法,搞出两个求导后的式子,令他们均为0,然后带入这个方程就行了.计算的复杂度为\(O(1)\).

容易发现增删直线对于系数的更改都是\(O(1)\)的.

于是我们就在\(O(m)\)的时间内解决了.

(对于系数的判断很"简单")

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
    
typedef double f2;
    
#define N 120010
f2 a[N],b[N],c[N],A,B,C,D,E,F;
   
void print(f2 x){
    if(fabs(x)<1e-6)puts("0.00");else printf("%.2lf\n",fabs(x));
}
 
f2 G[3][4],ansx,ansy;
inline void Solve(){
    register int i,j,k;f2 t;int n=2;
    if(fabs(G[1][1])<1e-6){
        if(fabs(G[1][2])<1e-6)ansx=0,ansy=G[2][3]/G[2][2];
        else ansy=G[1][3]/G[1][2],ansx=(G[2][3]-G[2][2]*ansy)/G[2][1];
        return;
    }
    if(fabs(G[2][2])<1e-6){
        if(fabs(G[2][1])<1e-6)ansx=G[1][3]/G[1][1],ansy=0;
        else ansx=G[2][3]/G[2][1],ansy=(G[1][3]-G[1][1]*ansx)/G[1][2];
        return;
    }
    t=-G[2][1]/G[1][1];G[2][1]=0,G[2][2]+=t*G[1][2],G[2][3]+=t*G[1][3];
    G[2][3]=(fabs(G[2][2])<1e-6)?0:G[2][3]/G[2][2],G[1][3]-=G[1][2]*G[2][3],G[1][3]/=G[1][1];
    ansx=G[1][3],ansy=G[2][3];
}
 
int main(){
    f2 x0,y0,x1,y1,t;int num=0,cnt,now=0;
    int q,qte,del;scanf("%d",&q);
    while(q--){
        scanf("%d",&qte);
        if(qte==0){
            scanf("%lf%lf%lf%lf",&x0,&y0,&x1,&y1);cnt=++num;++now;
            a[cnt]=y0-y1,b[cnt]=x1-x0,c[cnt]=y0*(x0-x1)-x0*(y0-y1),t=a[cnt]*a[cnt]+b[cnt]*b[cnt];
            A+=a[cnt]*a[cnt]/t,B+=b[cnt]*b[cnt]/t;C+=2*a[cnt]*b[cnt]/t;
            D+=2*a[cnt]*c[cnt]/t,E+=2*b[cnt]*c[cnt]/t,F+=c[cnt]*c[cnt]/t;
        }
        else if(qte==1){
            scanf("%d",&del);cnt=del,t=a[cnt]*a[cnt]+b[cnt]*b[cnt],--now;
            A-=a[cnt]*a[cnt]/t,B-=b[cnt]*b[cnt]/t,C-=2*a[cnt]*b[cnt]/t;
            D-=2*a[cnt]*c[cnt]/t,E-=2*b[cnt]*c[cnt]/t,F-=c[cnt]*c[cnt]/t;
        }
        else{
            if(now==0){puts("0.00");continue;}
            G[1][1]=2*A,G[1][2]=C,G[1][3]=-D,G[2][1]=C,G[2][2]=2*B,G[2][3]=-E;
            Solve();
            print(A*ansx*ansx+B*ansy*ansy+C*ansx*ansy+D*ansx+E*ansy+F);
        }
    }
    return 0;
}