BZOJ2829:信用卡凸包 计算几何+凸包+坐标变换

思路:

我们发现最终的答案就是缩小一圈后的矩形的顶点们形成的凸包的周长再加上一个半径为\(r\)的圆的周长.

因为多边形外角和为\(360\)度.这个不用多说.

 

此外还有一点点问题就是旋转后的坐标变换.

如果是\((x,y)\)以原点为旋转中心逆时针旋转\(\alpha\)弧度,那么新的坐标为\((xcos\alpha-ysin\alpha,xsin\alpha+ycos\alpha)\).

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
 
typedef double f2;
 
#define N 200010
 
static const f2 eps=1e-8;
inline int dcmp(const f2&x){return (fabs(x)<eps)?0:(x<0?-1:1);}
 
struct Point{
    f2 x,y;
    Point(){}
    Point(f2 _x,f2 _y):x(_x),y(_y){}
     
    Point operator+(const Point&B)const{return Point(x+B.x,y+B.y);}
    Point operator-(const Point&B)const{return Point(B.x-x,B.y-y);}
    Point operator*(const f2&p)const{return Point(p*x,p*y);}
    Point operator/(const f2&p)const{return Point(x/p,y/p);}
     
    bool operator<(const Point&B)const{return dcmp(x-B.x)==0?(dcmp(y-B.y)<0):(dcmp(x-B.x)<0);}
}P[N<<2],PP[N<<2],stk[N<<2];
 
f2 getdis(const Point&a,const Point&b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
Point move(const Point&p,const f2&Ang,const f2&x,const f2&y){
    return Point(p.x*cos(Ang)-p.y*sin(Ang)+x,p.x*sin(Ang)+p.y*cos(Ang)+y);
}
 
int main(){
    int n;scanf("%d",&n);
     
    f2 a,b,r,x,y,Ang;scanf("%lf%lf%lf",&a,&b,&r);a-=2*r,b-=2*r;
     
    register int i,j;
    int cnt=0;
    for(i=1;i<=n;++i){
        scanf("%lf%lf%lf",&x,&y,&Ang);
         
        P[++cnt]=move(Point(b/2,a/2),Ang,x,y);
        P[++cnt]=move(Point(-b/2,a/2),Ang,x,y);
        P[++cnt]=move(Point(b/2,-a/2),Ang,x,y);
        P[++cnt]=move(Point(-b/2,-a/2),Ang,x,y);
    }
     
    int top=0;
    sort(P+1,P+cnt+1);
    int revins;for(revins=cnt;revins!=1&&P[revins].x==P[revins-1].x;--revins);
    for(i=revins;i<=(n+revins)/2;++i)swap(P[i],P[n+revins-i]);
     
    for(i=1;i<=cnt;++i){
        if(!top)stk[++top]=P[i];
        else{while(top>1&&(P[i].y-stk[top].y)*(stk[top].x-stk[top-1].x)<=(stk[top].y-stk[top-1].y)*(P[i].x-stk[top].x))--top;stk[++top]=P[i];}
    }
    int nowtop=top;
    for(i=revins-1;i>=1;--i){
        if(top==nowtop)stk[++top]=P[i];
        else{while(top>=nowtop+1&&(P[i].y-stk[top].y)*(stk[top].x-stk[top-1].x)<=(stk[top].y-stk[top-1].y)*(P[i].x-stk[top].x))--top;stk[++top]=P[i];}
    }--top;
     
    f2 re=0;
    for(i=1;i<top;++i)re+=getdis(stk[i],stk[i+1]);re+=getdis(stk[top],stk[1]);
     
    printf("%.2lf",re+2*acos(-1.0)*r);
     
    return 0;
}

[POI2007]对称轴Axes of Symmetry-osi 计算几何+KMP

思路:

网上的题解也有很多了,主要介绍一下我得到的姿势.

我们抽象出边和点的信息,将多边形成一个长度为\(2n\)的字符串.

然后我们考虑有多少个这个字符串的循环同构串与这个串的反串相同.每出现一个就有一个对称轴.

想想好像是挺显然的.

#include<cstdio>
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
 
typedef long long LL;
 
#define N 100010
 
struct Point{
    int x,y;
    inline void read(){scanf("%d%d",&x,&y);}
    Point(){}
    Point(int _x,int _y):x(_x),y(_y){}
    Point operator-(const Point&B)const{return Point(B.x-x,B.y-y);}
}P[N];
inline LL Cross(const Point&a,const Point&b){return(LL)a.x*b.y-(LL)a.y*b.x;}
inline LL sqr(const int&x){return(LL)x*x;}
inline LL dis2(const Point&a,const Point&b){return sqr(a.x-b.x)+sqr(a.y-b.y);}
inline LL abs(const LL&x){return x<0?-x:x;}
LL seq1[N<<2],seq2[N<<1];
 
int n;
inline int f(int x){if(x<=0)return n;else if(x>n)return 1;else return x;}
 
int pre[N<<1];
int main(){
    int cas;scanf("%d",&cas);register int i,j;
    while(cas--){
        scanf("%d",&n);for(i=1;i<=n;++i)P[i].read();
        int cnt=0;for(i=1;i<=n;++i)seq1[++cnt]=Cross(P[f(i-1)]-P[i],P[i]-P[f(i+1)])|(1LL<<62),seq1[++cnt]=dis2(P[i],P[f(i+1)]);
         
        for(i=1;i<=cnt;++i)seq2[i]=seq1[cnt-i+1];
        for(i=cnt+1;i<=2*cnt;++i)seq1[i]=seq1[i-cnt];
         
        for(j=pre[1]=0,i=2;i<=cnt;++i){
            while(j&&seq2[j+1]!=seq2[i])j=pre[j];
            if(seq2[j+1]==seq2[i])++j;pre[i]=j;
        }
        int res=0;
        for(i=1,j=0;i<2*cnt;++i){
            while(j&&seq2[j+1]!=seq1[i])j=pre[j];
            if(seq2[j+1]==seq1[i])++j;if(j==cnt)++res;
        }
        printf("%d\n",res);
    }return 0;
}

BZOJ2956:模积和 数学

思路:

其实是一道简单题.关键是要注意到题目中的一个条件:\(i\not=j\)!!!

我们考虑用所有的情况减去\(i=j\)的情况.

所有的情况就很好求了.令\(f(n)=\sum_{i=1}^{n}n\%i\),则所有的情况为\(f(n)*{f(m)}\).

这玩意很容易分块求.

然后\(i=j\)的情况的贡献是:

\((n\%i)*(m\%i)=(n-i*\frac{n}{i})*(m-i*\frac{m}{i})=nm+i^2\frac{n}{i}\frac{m}{i}-n*i\frac{m}{i}-m*i\frac{n}{i}\)

这样发现分开之后都可以分块求.

于是就\(O(\sqrt{n})\)水过.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
typedef long long LL;
 
static const int mod=19940417,rev2=(mod+1)>>1,rev6=3323403;
 
inline void mul(int&x,int y){x=(LL)x*y%mod;}
inline void inc(int&x,int y){if((x+=y)>=mod)x-=mod;}
inline void dec(int&x,int y){x=x<y?x+mod-y:x-y;}
inline int calc(int n){//return sigma i^2
    int res=n;mul(res,(LL)(n+1)*(2*n+1)%mod),mul(res,rev6);return res;
}
inline int cal(int l,int r){
    int cl=calc(l-1),cr=calc(r);
    return cr<cl?cr+mod-cl:cr-cl;
}
 
inline int solve1(int n,int lim){//return sigma i*(n/i)
    int lst,t,res=0;
    for(int i=1;i<=lim;i=lst+1){
        lst=min(lim,n/(n/i));
        mul(t=n/i,(LL)(i+lst)*(lst-i+1)%mod),mul(t,rev2);
        inc(res,t);
    }return res;
}
inline int solve2(int n){//return sigma n%i
    return ((LL)n*n%mod+mod-solve1(n,n))%mod;
}
inline int solve3(int n,int m,int lim){//return sigma i^2(n/i)(m/i)
    int lst,t,res=0;
    for(int i=1;i<=lim;i=lst+1){
        lst=min(n/(n/i),m/(m/i)),lst=min(lst,lim);
        mul(t=(LL)(n/i)*(m/i)%mod,cal(i,lst));
        inc(res,t);
    }return res;
}
 
int main(){
    int n,m;scanf("%d%d",&n,&m);register int i,j;
    int res=(LL)solve2(n)*solve2(m)%mod;
    dec(res,(LL)min(n,m)*n%mod*m%mod);
    inc(res,(LL)solve1(n,min(n,m))*m%mod);
    inc(res,(LL)solve1(m,min(n,m))*n%mod);
    dec(res,solve3(n,m,min(n,m)));
    printf("%d",res);
    return 0;
}

BZOJ1189:[HNOI2007]紧急疏散evacuate 二分+最大流

思路:

首先bfs求出每个空地到每个门的最短距离,注意中间不能经过任何门.好像是原题说过只要经过门就算是退出了.

随后我们二分答案,从原点到每块空地连容量为1的边,从每扇门到汇点连容量为(当前二分的答案)时间的边,对于每块空地和每扇门,若距离不超过时间,则从空地到门连一条容量为1的边.

好像唯一的trick就是从门到汇点的边?不过大家自己YY一下吧.

反正不难.

#include<cstdio>
#include<cstring>
#include<queue>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
#define INF 0x3f3f3f3f
 
#define N 21
#define M 21
int n,m;
char s[M];
 
int ty[N][M];
 
int d[N][M];
 
static const int dx[]={-1,1,0,0},dy[]={0,0,1,-1};
struct Ins{
    int x,y;
    Ins(){}
    Ins(int _x,int _y):x(_x),y(_y){}
};
void bfs(int x,int y){
    static queue<Ins>q;
    d[x][y]=0,q.push(Ins(x,y));
    while(!q.empty()){
        Ins tmp=q.front();q.pop();
        for(int k=0;k<4;++k){
            int nx=tmp.x+dx[k],ny=tmp.y+dy[k];
            if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&ty[nx][ny]==1&&d[nx][ny]==-1)
                d[nx][ny]=d[tmp.x][tmp.y]+1,q.push(Ins(nx,ny));
        }
    }
}
 
struct MaxflowSolver{
    static const int V=1010,E=200010;
    int head[V],next[E],end[E],flow[E],ind,d[V],inq[V];
    inline void reset(){ind=0,memset(head,-1,sizeof head);}
    inline void addedge(int a,int b,int f){int q=ind++;end[q]=b,next[q]=head[a],head[a]=q,flow[q]=f;}
    inline void make(int a,int b,int f){addedge(a,b,f),addedge(b,a,0);}
    inline bool bfs(int S,int T){
        static int q[V];int fr=0,ta=0;
        memset(d,-1,sizeof d),d[S]=0,q[ta++]=S;
        while(fr^ta){
            int i=q[fr++];for(int j=head[i];j!=-1;j=next[j])if(flow[j]&&d[end[j]]==-1)d[end[j]]=d[i]+1,q[ta++]=end[j];
        }return d[T]!=-1;
    }
    inline int dinic(int p,int T,int Maxflow){
        if(p==T)return Maxflow;
        int last=Maxflow;
        for(int j=head[p];j!=-1;j=next[j])if(flow[j]&&d[end[j]]==d[p]+1){
            int to=dinic(end[j],T,last>flow[j]?flow[j]:last);
            if(to){flow[j]-=to,flow[j^1]+=to;if(!(last-=to))return Maxflow;}
        }d[p]=-1;return Maxflow-last;
    }
    inline int Maxflow(int S,int T){
        int res=0;while(bfs(S,T))res+=dinic(S,T,INF);return res;
    }
}Stg;
 
int lab[N][M];
 
int main(){
    scanf("%d%d",&n,&m);register int i,j;
    for(i=1;i<=n;++i){
        scanf("%s",s+1);
        for(j=1;j<=m;++j)if(s[j]=='.')ty[i][j]=1;else if(s[j]=='D')ty[i][j]=2;
    }
     
    int id=0;for(i=1;i<=n;++i)for(j=1;j<=m;++j)if(ty[i][j])lab[i][j]=++id;
     
    int L=0,R=1<<30,mid;
    while(L<R){
        mid=(L+R)>>1;
        Stg.reset();
        int cnt=0;
        for(i=1;i<=n;++i)for(j=1;j<=m;++j)if(ty[i][j]==1)Stg.make(0,lab[i][j],1),++cnt;
        for(i=1;i<=n;++i)for(j=1;j<=m;++j)if(ty[i][j]==2)Stg.make(lab[i][j],n*m+1,mid);
        for(i=1;i<=n;++i)for(j=1;j<=m;++j)if(ty[i][j]==2){
            memset(d,-1,sizeof d),bfs(i,j);
            for(int p=1;p<=n;++p)for(int q=1;q<=m;++q)if(ty[p][q]==1&&d[p][q]!=-1)
                if(d[p][q]<=mid)Stg.make(lab[p][q],lab[i][j],1);
        }
        if(Stg.Maxflow(0,n*m+1)==cnt)R=mid;else L=mid+1;
    }
     
    if(L==1<<30)puts("impossible");else printf("%d",L);
    return 0;
}

BZOJ2786:Ural1142 Relation 递推+高精度

思路:

令\(f[i][j]\)表示将\(i\)个数划分为\(j\)个集合的方案数.

则有递推式:

\[f[i][j]=f[i-1][j]*j+f[i-1][j-1]\]

于是\(ans(n)=\sum_{i=1}^{n}f[n][i]*fac(i)\)

数据范围小,随便写写.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
static const int mod=1e4;
struct Int{
    int d[100],l;
    Int():l(1){memset(d,0,sizeof d);}
    inline void operator=(const int&x){memset(d,0,sizeof d),l=1,d[0]=x;}
    inline void operator+=(const Int&B){
        l=l>B.l?l:B.l;
        for(int i=0;i<l;++i){
            d[i]+=B.d[i];
            if(d[i]>=mod)d[i+1]+=d[i]/mod,d[i]%=mod;
        }if(d[l])l++;
    }
    inline void operator*=(const int&x){
        int p=0;
        for(int i=0;i<l;++i)d[i]=(p+=d[i]*x)%mod,p/=mod;
        if(p)d[l++]=p;
    }
    inline Int operator*(const Int&B){
        Int C;
        for(int i=0;i<l;++i)for(int j=0;j<B.l;++j){
            C.d[i+j]+=d[i]*B.d[j];if(C.d[i+j]>=mod)C.d[i+j+1]+=C.d[i+j]/mod,C.d[i+j]%=mod;
        }
        for(C.l=l+B.l+1;!C.d[C.l-1];--C.l);
        return C;
    }
    inline void output(){
        printf("%d",d[l-1]);for(int i=l-2;i>=0;--i)printf("%04d",d[i]);
    }
};
 
Int f[51][51],fac[51],res[51];
 
int main(){
    int i,j;
    for(fac[0]=1,fac[1]=1,i=2;i<=50;++i)fac[i]=fac[i-1],fac[i]*=i;
    for(i=1;i<=50;++i){
        f[i][1]=1,f[i][i]=1;
        for(j=2;j<i;++j)f[i][j]=f[i-1][j],f[i][j]*=j,f[i][j]+=f[i-1][j-1];
    }
     
    for(i=1;i<=50;++i)for(j=1;j<=i;++j)res[i]+=f[i][j]*fac[j];
     
    int T;scanf("%d",&T);
    int x;while(T--)scanf("%d",&x),res[x].output(),puts("");
     
    return 0;
}

BZOJ2212:[Poi2011]Tree Rotations 平衡树启发式合并

思路:

显然左右子树内的逆序对并不会互相影响,那么会产生影响的仅仅是是否交换左右子树.

我们利用平衡树启发式合并,每一次用小的平衡树在大的平衡树里面询问小的有多少以及大的有多少决定是否交换.

然后暴力将小的平衡树合并到大的平衡树里面.

(貌似还可以用线段树启发式合并?不过我并不想写,虽然常数小)

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

typedef long long LL;

#define l ch[0]
#define r ch[1]

#define N 200010
struct Node{
	Node*ch[2];
	int v,siz,pr;
	Node():siz(0){}
	inline void up(){siz=l->siz+r->siz+1;}
}mem[4000010],*C=mem,Tnull,*null=&Tnull;
inline Node*Newnode(int _v){
	C->l=C->r=null;C->v=_v,C->siz=1,C->pr=rand();return C++;
}
inline void Rot(Node*&p,bool d){
	Node*q=p->ch[!d];p->ch[!d]=q->ch[d],q->ch[d]=p,p->up(),q->up(),p=q;
}
inline void Insert(Node*&p,int x){
	if(p==null){p=Newnode(x);return;}
	if(x<p->v){Insert(p->l,x);if(p->l->pr<p->pr)Rot(p,1);}
	else{Insert(p->r,x);if(p->r->pr<p->pr)Rot(p,0);}
	p->up();
}
inline int QueryLess(Node*p,int x){
	if(p==null)return 0;
	if(p->v>x)return QueryLess(p->l,x);
	else return p->l->siz+1+QueryLess(p->r,x);
}
inline int QueryMore(Node*p,int x){
	if(p==null)return 0;
	if(p->v<x)return QueryMore(p->r,x);
	else return p->r->siz+1+QueryMore(p->l,x);
}

int L[N<<1],R[N<<1],cnt,x,w[N<<1];
inline int read(){
	int q=++cnt;
	scanf("%d",&x);
	if(!x)L[q]=read(),R[q]=read();else w[q]=x;
	return q;
}

LL res=0,tans1,tans2;

Node*Root[N<<1];
static Node*q[N<<1];int fr,ta;
void dfs(int x){
	if(w[x]){Root[x]=Newnode(w[x]);return;}
	dfs(L[x]),dfs(R[x]);
	tans1=tans2=0;
	if(Root[L[x]]->siz<Root[R[x]]->siz){
		fr=ta=0;q[ta++]=Root[L[x]];
		while(fr^ta){
			Node*p=q[fr++];
			tans1+=QueryLess(Root[R[x]],p->v),tans2+=QueryMore(Root[R[x]],p->v);
			if(p->l!=null)q[ta++]=p->l;if(p->r!=null)q[ta++]=p->r;
		}
		res+=min(tans1,tans2);
		fr=ta=0;q[ta++]=Root[L[x]];
		while(fr^ta){
			Node*p=q[fr++];
			Insert(Root[R[x]],p->v);
			if(p->l!=null)q[ta++]=p->l;if(p->r!=null)q[ta++]=p->r;
		}
		Root[x]=Root[R[x]];
	}
	else{
		fr=ta=0;q[ta++]=Root[R[x]];
		while(fr^ta){
			Node*p=q[fr++];
			tans1+=QueryLess(Root[L[x]],p->v),tans2+=QueryMore(Root[L[x]],p->v);
			if(p->l!=null)q[ta++]=p->l;if(p->r!=null)q[ta++]=p->r;
		}
		res+=min(tans1,tans2);
		fr=ta=0;q[ta++]=Root[R[x]];
		while(fr^ta){
			Node*p=q[fr++];
			Insert(Root[L[x]],p->v);
			if(p->l!=null)q[ta++]=p->l;if(p->r!=null)q[ta++]=p->r;
		}
		Root[x]=Root[L[x]];
	}
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("tt.in","r",stdin);
#endif
	scanf("%d",&x);read();
	
	dfs(1);
	
	cout<<res<<endl;
	
	return 0;
}

BZOJ1187:[HNOI2007]神奇游乐园 插头dp

思路:

非常裸的插头dp,只不过这道题是只有一条回路,这个很容易实现,当遇到左右插头合并的时候直接更新答案,而并不是向下转移.

然后这个是要维护一个左右插头,我们用三进制来维护.

具体转移时有几个细节:

不能在边界上插插头!

更新答案时必须确认当前只有左右插头各一个!

我写了一个结构体来维护插头,还是蛮好写的.

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

int l;
struct Line{
	int d[7];
	Line(){memset(d,0,sizeof d);}
	inline int hash(){
		int re=0,mi=1;for(int i=0;i<l;++i,mi*=3)re+=d[i]*mi;return re;
	}
	inline void flush(){
		for(int i=l-1;i>=1;--i)d[i]=d[i-1];d[0]=0;
	}
	inline int getleftins(int x){
		int sav=0;
		for(int i=x;i>=0;--i){
			if(d[i]==2)++sav;else if(d[i]==1)--sav;
			if(sav==0)return i;
		}
	}
	inline int getrightins(int x){
		int sav=0;
		for(int i=x;i<l;++i){
			if(d[i]==1)++sav;else if(d[i]==2)--sav;
			if(sav==0)return i;
		}
	}
	Line change(int ins,int x){
		Line re=*this;re.d[ins]=x;return re;
	}
	inline int getlnum(){
		int re=0;for(int i=0;i<l;++i)re+=d[i]==1;return re;
	}
	inline int getrnum(){
		int re=0;for(int i=0;i<l;++i)re+=d[i]==2;return re;
	}
};
struct Hashset{
	int ins[2187],w[2187],ind;
	Line s[2187];
	inline void reset(){ind=0,memset(ins,-1,sizeof ins);}
	inline void insert(Line l,int _w){
		int hash=l.hash();
		if(ins[hash]==-1)ins[hash]=ind,w[ind]=_w,s[ind++]=l;
		else w[ins[hash]]=max(w[ins[hash]],_w);
	}
}S[2];

#define N 110
#define M 10
int w[N][M];

int main(){
	int n,m;scanf("%d%d",&n,&m);register int i,j,k;l=m+1;
	for(i=1;i<=n;++i)for(j=1;j<=m;++j)scanf("%d",&w[i][j]);
	
	int now=0,last=1;
	Line begin;S[now].reset(),S[now].insert(begin,0);
	
	int res=-1<<30;
	Line nowst;int nowf,d1,d2,ins1,ins2;
	for(i=1;i<=n;++i){
		for(j=1;j<=m;++j){
			swap(now,last),S[now].reset();
			for(k=0;k<S[last].ind;++k){
				nowst=S[last].s[k];nowf=S[last].w[k];
				d1=nowst.d[j-1],d2=nowst.d[j];
				if(d1==0&&d2==0){
					S[now].insert(nowst,nowf);
					if(i!=n&&j!=m)S[now].insert(nowst.change(j-1,1).change(j,2),nowf+w[i][j]);
				}
				else if(d1==0||d2==0){
					if(i!=n)S[now].insert(nowst.change(j-1,d1+d2).change(j,0),nowf+w[i][j]);
					if(j!=m)S[now].insert(nowst.change(j-1,0).change(j,d1+d2),nowf+w[i][j]);
				}
				else if(d1==1&&d2==1){
					ins1=nowst.getrightins(j),ins2=nowst.getrightins(j-1);
					S[now].insert(nowst.change(j-1,0).change(j,0).change(ins1,1).change(ins2,2),nowf+w[i][j]);
				}
				else if(d1==1&&d2==2){
					if(nowst.getlnum()==1&&nowst.getrnum()==1)res=max(res,nowf+w[i][j]);
				}
				else if(d1==2&&d2==1){
					S[now].insert(nowst.change(j-1,0).change(j,0),nowf+w[i][j]);
				}
				else if(d1==2&&d2==2){
					ins1=nowst.getleftins(j),ins2=nowst.getleftins(j-1);
					S[now].insert(nowst.change(j-1,0).change(j,0).change(ins1,1).change(ins2,2),nowf+w[i][j]);
				}
			}
		}
		swap(now,last),S[now].reset();
		for(k=0;k<S[last].ind;++k){
			nowst=S[last].s[k],nowf=S[last].w[k];
			nowst.flush();
			S[now].insert(nowst,nowf);
		}
	}
	
	printf("%d",res);
	
	return 0;
}
		

BZOJ1822:[JSOI2010]Frozen Nova 冷冻波 二分+计算几何+网络流

思路:

一眼网络流,套一个二分就行了.

关键是如何判断巫妖和精灵之间能否有圆阻挡.

一开始我是进行了复杂的判断:如果线段平行于x轴或者平行于y轴直接特判;

否则求出圆心到直线的距离,如果距离不超过半径,则用一个简单的公式求出圆心到直线最近点的横坐标,利用这个横坐标与线段两个端点的横坐标比较,得出结果.

可是数据跟我想的根本不是一回事啊啊啊!

根本只需要判断距离啊啊啊!!而且相切的情况不算相交啊啊啊!!

什么坑爹数据,我是领教到了.

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

typedef long long LL;

#define INF 0x3f3f3f3f

#define N 210
#define M 210
#define P 210

struct MaxflowSolver{
	static const int V=410;
	static const int E=100010;
	int head[V],next[E],end[E],flow[E],ind,d[V];
	inline void reset(){ind=0,memset(head,-1,sizeof head);}
	inline void addedge(int a,int b,int f){int q=ind++;end[q]=b,next[q]=head[a],head[a]=q,flow[q]=f;}
	inline void make(int a,int b,int f){addedge(a,b,f),addedge(b,a,0);}
	inline bool bfs(int S,int T){
		static int q[V];int fr=0,ta=0;
		memset(d,-1,sizeof d),d[S]=0,q[ta++]=S;
		while(fr^ta){
			int i=q[fr++];for(int j=head[i];j!=-1;j=next[j])if(flow[j]&&d[end[j]]==-1)d[end[j]]=d[i]+1,q[ta++]=end[j];
		}return d[T]!=-1;
	}
	inline int dinic(int p,int T,int Maxflow){
		if(p==T)return Maxflow;
		int last=Maxflow;
		for(int j=head[p];j!=-1;j=next[j])if(flow[j]&&d[end[j]]==d[p]+1){
			int to=dinic(end[j],T,last>flow[j]?flow[j]:last);
			if(to){flow[j]-=to,flow[j^1]+=to;if(!(last-=to))return Maxflow;}
		}d[p]=-1;return Maxflow-last;
	}
	inline int Maxflow(int S,int T){
		int res=0;while(bfs(S,T))res+=dinic(S,T,INF);return res;
	}
}Stg;
int x1[N],y1[N],x2[M],y2[M],lim[N],cd[N],ox[P],oy[P],r[P];

int G[N][M];

inline int sqr(int x){return x*x;}



int main(){
	int n,m,p;scanf("%d%d%d",&n,&m,&p);register int i,j,k;
	for(i=1;i<=n;++i)scanf("%d%d%d%d",&x1[i],&y1[i],&lim[i],&cd[i]);
	for(i=1;i<=m;++i)scanf("%d%d",&x2[i],&y2[i]);
	for(i=1;i<=p;++i)scanf("%d%d%d",&ox[i],&oy[i],&r[i]);
	
	int a,b,c;
	for(i=1;i<=n;++i)for(j=1;j<=m;++j)if(sqr(x1[i]-x2[j])+sqr(y1[i]-y2[j])<=sqr(lim[i])){
		bool ok=1;
		for(k=1;k<=p&&ok;++k){
			a=y1[i]-y2[j],b=x2[j]-x1[i],c=(x1[i]-x2[j])*y1[i]-(y1[i]-y2[j])*x1[i];
			if((LL)(a*ox[k]+b*oy[k]+c)*(a*ox[k]+b*oy[k]+c)<(LL)r[k]*r[k]*(a*a+b*b))ok=0;
		}
		if(ok)G[i][j]=1;
	}
	
	int L=0,R=1<<30,mid;
	while(L<R){
		mid=(L+R)>>1;
		Stg.reset();
		for(i=1;i<=n;++i)Stg.make(0,i,mid/cd[i]+1);
		for(i=1;i<=m;++i)Stg.make(n+i,n+m+1,1);
		for(i=1;i<=n;++i)for(j=1;j<=m;++j)if(G[i][j])Stg.make(i,n+j,1);
		if(Stg.Maxflow(0,n+m+1)==m)R=mid;else L=mid+1;
	}
	
	if(L==1<<30)puts("-1");else printf("%d",L);
	return 0;
}

BZOJ3413:匹配 后缀自动机

思路:

这道题真心是一道字符串好题.我要不是膜拜了秦神的代码现在还看不出个所以然...(果然太弱不解释)

 

首先呢,题目的意思其实就是让我们求询问串与原串的每一个后缀的LCP之和.但是呢,有一个Trick,那就是找到整个串之后就不找啦!

那么先考虑一种简单的情况,就是原串中并不存在询问串.

那么,我们在逆序后缀树上走,每走到一个位置,就看一下当前串出现了几次.这样,实际上每次统计的都是这一位的贡献.

现在有限制.我们可以预先处理出询问串第一次出现的位置.那么我们可以离线处理,利用dfs序来维护逆序后缀树,然后将所有询问按照限制从小到大累加贡献.

 

有点不好说.详情见代码.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
  
typedef long long LL;
  
#define N 100010
#define M 3000010
#define Q 500010
int tranc[N<<1][10],pa[N<<1],len[N<<1],first[N<<1],ins[N],cnt,root,last;
inline int newnode(int l){
    len[++cnt]=l;return cnt;
}
  
char s[N];
  
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++;
    }
    inline bool digit(char c){return c>='0'&&c<='9';}
    template<typename T>inline void Get(T&x){
        int c;while(!digit(c=getc()));x=c-'0';while(digit(c=getc()))x=(x<<1)+(x<<3)+c-'0';
    }
    char buf[500010*12],*o=buf;
    template<typename T>inline void print(T x){
        static int stk[100];int top=0;
        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 flush(){fwrite(buf,1,o-buf,stdout);}
}
using namespace Fio;
  
namespace Tree{
    int head[N<<1],next[N<<1],end[N<<1];
    inline void addedge(int a,int b){
        static int q=1;end[q]=b,next[q]=head[a],head[a]=q++;
    }
    int in[N<<1],out[N<<1],tclock;
    inline void dfs(int x){
        in[x]=++tclock;
        for(int j=head[x];j;j=next[j])dfs(end[j]);
        out[x]=tclock;
    }
    int A[N<<1];
    inline int ask(int x){int r=0;for(;x;x-=x&-x)r+=A[x];return r;}
    inline void mdf(int x,int add){for(;x<=cnt;x+=x&-x)A[x]+=add;}
    inline void addnode(int x){mdf(in[x],1);}
    inline int asksubtree(int x){return ask(out[x])-ask(in[x]-1);}
}
 
namespace Query{
    int head[N],next[M],end[M],lab[M];
    inline void insert(int a,int b,int _lab){
        static int q=1;end[q]=b,next[q]=head[a],head[a]=q,lab[q++]=_lab;
    }
}
  
LL res[Q];
char Read[100010];
  
char ch;
int main(){
    int n;Get(n);register int i,j;
      
    int id=0;while(!digit(ch=getc()));s[++id]=ch;
    while(digit(ch=getc()))s[++id]=ch;  
     
    root=last=newnode(0);
    int p,q,np,nq,y;
    for(i=1;i<=n;++i){
        ins[i]=np=newnode(len[last]+1);
        for(y=s[i]-'0',p=last;p&&!tranc[p][y];p=pa[p])tranc[p][y]=np;
        if(!p)pa[np]=root;
        else{
            q=tranc[p][y];
            if(len[q]==len[p]+1)pa[np]=q;
            else{
                nq=newnode(len[p]+1),pa[nq]=pa[q],pa[q]=pa[np]=nq;
                memcpy(tranc[nq],tranc[q],sizeof tranc[q]);
                for(;p&&tranc[p][y]==q;p=pa[p])tranc[p][y]=nq;
            }
        }last=np;
    }
      
 
    for(i=1;i<=n;++i)for(p=ins[i];p;p=pa[p])if(!first[p])first[p]=i;else break;
     
    for(i=1;i<=cnt;++i)if(pa[i])Tree::addedge(pa[i],i);
    Tree::dfs(1);
     
     
     
    int cas;Get(cas);
    for(i=1;i<=cas;++i){
        int len=0;while(!digit(ch=getc()));Read[++len]=ch;
        while(digit(ch=getc()))Read[++len]=ch;
         
        bool find=1;int p=root;
        for(j=1;j<=len;++j){
            y=Read[j]-'0';if(tranc[p][y])p=tranc[p][y];else{find=0;break;}
        }
        if(!find){
            res[i]=n;
            for(p=root,j=1;j<=len;++j){
                y=Read[j]-'0';if(tranc[p][y])p=tranc[p][y],Query::insert(n,p,i);else break;
            }
        }
        else{
            res[i]=first[p]-len;
            int firstins=first[p];
            for(p=root,j=1;j<=len;++j){
                y=Read[j]-'0',Query::insert(firstins-len+j,p=tranc[p][y],i);
            }
        }
    }
     
    for(i=1;i<=n;++i){
        Tree::addnode(ins[i]);
        for(j=Query::head[i];j;j=Query::next[j])res[Query::lab[j]]+=Tree::asksubtree(Query::end[j]);
    }
      
    for(i=1;i<=cas;++i)Fio::print(res[i]);
 
    Fio::flush();
    return 0;
}

BZOJ3511:土地划分 网络流

思路:

直接套用二元关系最小割,对于这道题而言非常朴素,而且不用考虑某些变量为负数还需要构造二分图的情况,很容易就水过去了.

现在我们仅考虑二元关系:(我们将所有收益预先加到一起,那么现在仅是需要减去最小的损失)

考虑两个点\(x,y\)以及一条边\(E(x,y)\):

\(x\in{A},y\in{A},cost=C_1=E_B\)

\(x\in{A},y\in{B},cost=C_2=E_A+E_B+E_C\)

\(x\in{B},y\in{A},cost=C_3=E_A+E_B+E_C\)

\(x\in{A},y\in{B},cost=C_4=E_A\)

连边\((S,x,a),(S,y,b),(x,T,c),(y,T,d),(x,y,e),(y,x,f)\).

随后不难发现:

\(C_1=c+d,C_2=b+c+e,C_3=a+d+f,C_4=a+b\)

好辣!如果我们能够直接解出\(a,b,c,d,e,f\)不是就非常好了么.

注意到如果\(a,b,c,d\)是负数我们并不用在意,因为\(a,c\)中只能选择一个,\(b,d\)中也只能选择一个,所以我们可以将边权均增大一个值,最终再减去即可.

可是\(e,f\)并不可以这样草率处理.

当\(C_2+C_3-C_1-C_2>0\)时,\(e,f\)可以取大于0的数,而\(a,b,c,d\)随意选择,问题可以解决.

如果不是的话,我们就需要在一对二元关系中,将其中一个点的意义反向,当然这有一个条件,二元关系必须不能存在奇环.不过这都是后话了.

这道题我们显然不用构造二分图,直接列出式子见图就行了.为了避免实数网络流将边权扩大二倍.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
 
static const int INF=0x3f3f3f3f;
 
struct MaxflowSolver{
    static const int V=10010;
    static const int E=600010;
    int head[V],next[E],end[E],flow[E],ind;
    int gap[V],stk[V],d[V],top,cur[V];
    inline void reset(){ind=top=0,memset(head,-1,sizeof head);}
    inline void addedge(int a,int b,int f){int q=ind++;end[q]=b,next[q]=head[a],head[a]=q,flow[q]=f;}
    inline void make(int a,int b,int f){addedge(a,b,f),addedge(b,a,0);}
    inline void bfs(int T){
        static int q[V];int fr=0,ta=0;
        memset(gap,0,sizeof gap),memset(d,-1,sizeof d),++gap[d[T]=0],q[ta++]=T;
        while(fr^ta){
            int i=q[fr++];for(int j=head[i];j!=-1;j=next[j])if(d[end[j]]==-1)++gap[d[end[j]]=d[i]+1],q[ta++]=end[j];
        }
    }
    inline int Maxflow(int S,int T,int cnt){
        int res=0,u=S,ins,Min,i;bfs(T),memcpy(cur,head,sizeof cur);
        while(d[S]<cnt){
            if(u==T){
                for(Min=INF,i=0;i<top;++i)if(flow[stk[i]]<Min)Min=flow[stk[i]],ins=i;
                for(res+=Min,i=0;i<top;++i)flow[stk[i]]-=Min,flow[stk[i]^1]+=Min;
                u=end[stk[top=ins]^1];
            }
            if(u!=T&&!gap[d[u]-1])break;
            bool find=0;
            for(int&j=cur[u];j!=-1;j=next[j])if(flow[j]&&d[u]==d[end[j]]+1){find=1,ins=j;break;}
            if(find)stk[top++]=cur[u]=ins,u=end[ins];
            else{
                Min=cnt;for(int j=head[u];j!=-1;j=next[j])if(flow[j]&&d[end[j]]<Min)Min=d[end[j]],cur[u]=j;
                if(!--gap[d[u]])break;++gap[d[u]=Min+1];
                if(u!=S)u=end[stk[--top]^1];
            }
        }return res;
    }
}Stg;
 
int in[10010],out[10010];
 
int main(){
    int n,m;scanf("%d%d",&n,&m);register int i,j;
     
    Stg.reset();
    int S=0,T=n+1;
    in[1]=out[n]=INF;
    int a,b,x,res=0;
    for(i=2;i<n;++i)scanf("%d",&x),res+=x<<1,in[i]+=x<<1;
    for(i=2;i<n;++i)scanf("%d",&x),res+=x<<1,out[i]+=x<<1;
     
    int Ea,Eb,Ec;
    while(m--){
        scanf("%d%d%d%d%d",&a,&b,&Ea,&Eb,&Ec),res+=2*Ea+2*Eb;
        in[a]+=Ea,in[b]+=Ea,out[a]+=Eb,out[b]+=Eb;
        Stg.make(a,b,Ea+Eb+2*Ec),Stg.make(b,a,Ea+Eb+2*Ec);
    }
     
    for(i=1;i<=n;++i){
        if(in[i])Stg.make(S,i,in[i]);
        if(out[i])Stg.make(i,T,out[i]);
    }
     
    printf("%d",(res-Stg.Maxflow(S,T,n+2))>>1);
     
    return 0;
}