BZOJ2221:[Jsoi2009]面试的考验 单调性+随机数据

思路:

宋文杰的题解很详细.

一开始每个点30s,结果在OJ上一共30s...

也就是说原来莫队是能过的,结果我无悬念TLE.

莫队怎么做我就不说了.(貌似官方正解就是莫队...)

莫队很暴力,因为没有利用一个题目中的性质:就是数据随机生成!

询问随不随机意义是不大的.

但是如果询问很特殊,我们就可以用科学的方法去处理:

(1)任意两个区间不互相包含.这种情况我们将区间随便排个序就能发现总移动次数是\(O(n)\)的.

(2)任意两个区间要么互相包含,要么不相交.这样就会形成一棵树.我们利用启发式合并,插入次数就为\(O(nlogn)\).

但是随机数据显然没有这种性质.

 

那么唯一可能有比较优秀的性质的就是随机数列了.

考虑一种朴素算法:

将所有询问离线按照右端点从小到大排序.

从前向后依次加入右端点,令\(f_i\)表示当前以\(i\)为起点的后缀的答案,那么朴素来看,如果当前加入的是\(n\),那么\(f_1-f_{n-1}\)都需要更新.

但是有很多冗余的更新.

例如我们考虑所有大于\(a_n\)的数,不妨令\(a_i,a_j>a_n,a_i>a_j,i<j\),那么如果我们能够维护一个后缀的答案,我们就只需更新\(f_j\)就可以了.

于是我们发现了某些单调性.

我们利用数据结构维护后缀的答案,那么只需更新一些点就行了.

那么我们的任务就是找到,对于\(n\)之前的数,一个均\(>a_n\)的递减序列,一个均\(<a_n\)的递增序列.然后我们就拿序列中的东西暴力更新答案.

然而由于是随机数据,序列中的元素个数是\(O(logn)\)级别的.

这样总时间复杂度就是\(O(qlogn+nlog^2n)\).

具体去看宋文杰的题解.

(另外此题要求结果\(>0\) 23333)

 

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

#define N 100010
#define Q 100010
int n,q,a[N];

struct Interval{
	int l,r,lab;
	bool operator<(const Interval&B)const{return r<B.r;}
}S[N];
int re[Q];

inline void mini(int&x,int y){if(x>y)x=y;}
struct SegmentTree{
	int A[262144],M;
	inline void Init(int _siz){
		for(M=1;M<(_siz+2);M<<=1);
		memset(A,0x3f,sizeof A);
	}
	inline void modify(int x,int d){
		for(x+=M;x;x>>=1)mini(A[x],d);
	}
	inline int query(int tl,int tr){
		int re=~0U>>1;
		for(tl+=M-1,tr+=M+1;tl^tr^1;tl>>=1,tr>>=1){
			if(~tl&1)mini(re,A[tl^1]);
			if(tr&1)mini(re,A[tr^1]);
		}
		return re;
	}
}Seg;

int preless[N],premore[N],small[N][30],big[N][30],stk[N],top;

int main(){
	//freopen("tt.in","r",stdin);
	scanf("%d%d",&n,&q);
	register int i,j,k;
	
	for(i=1;i<=n;++i)scanf("%d",&a[i]);
	
	for(i=1;i<=q;++i)
		scanf("%d%d",&S[i].l,&S[i].r),S[i].lab=i;
	sort(S+1,S+q+1);
	
	Seg.Init(n);
	
	for(i=1;i<=n;++i){
		while(top&&a[stk[top]]>=a[i])--top;
		preless[i]=stk[top];
		stk[++top]=i;
	}
	top=0;
	for(i=1;i<=n;++i){
		while(top&&a[stk[top]]<=a[i])--top;
		premore[i]=stk[top];
		stk[++top]=i;
	}
	
	for(i=1;i<=n;++i){
		if(premore[i]){
			big[i][++big[i][0]]=premore[i];
			j=big[i][1];
			while(1){
				bool find=0;
				for(k=1;k<=small[j][0];++k)if(a[small[j][k]]>a[i]){find=1;break;}
				if(find)big[i][++big[i][0]]=(j=small[j][k]);else break;
			}
		}
		if(preless[i]){
			small[i][++small[i][0]]=preless[i];
			j=small[i][1];
			while(1){
				bool find=0;
				for(k=1;k<=big[j][0];++k)if(a[big[j][k]]<a[i]){find=1;break;}
				if(find)small[i][++small[i][0]]=(j=big[j][k]);else break;
			}
		}
	}
	j=1;
	for(i=1;i<=n;++i){
		for(k=1;k<=small[i][0];++k)Seg.modify(small[i][k],a[i]-a[small[i][k]]);
		for(k=1;k<=big[i][0];++k)Seg.modify(big[i][k],a[big[i][k]]-a[i]);
		
		for(;j<=q&&S[j].r==i;++j)re[S[j].lab]=Seg.query(S[j].l,S[j].r);
		
	}
	
	for(i=1;i<=q;++i)printf("%d\n",re[i]);
	
	return 0;
}

BZOJ2560:串珠子 状压dp

思路:

令\(f[S]\)表示集合S形成连通图的方案数,\(g[s]\)表示集合S内部任意连边的方案数.

显然:

\[g[S]=\prod_{i,j\in{S},i<j}c_{i,j}+1\]

这个我们可以在\(O(2^n{n^2})\)的复杂度内算出来.

接下来考虑计算\(f[S]\).

我们用总数减去不连通的图的个数.

假设\(x\in{S}\),我们不妨枚举所有\(S\)的真子集\(S'\),其中\(S'\)就代表包含\(x\)的一个连通块,\(S-S'\)均与\(x\)不连通.

那么\(f[S']\)我们已经算过了,剩下的\(S-S'\)就利用我们之前预处理的随意连边的\(g\)数组.

于是我们得到转移:

\[f[S]=\sum_{S'\subset{S},x\in{S'}}f[S']g[S-S']\]

这个过程是\(O(3^n)\).

然后我们就在\(O(2^n{n^2}+3^n)\)的时间内解决了这题.

 

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
typedef long long LL;
#define N 16
 
int f[1<<N],g[1<<N],h[1<<N],c[N][N];
static const int mod=(1e9)+7;
inline void inc(int&x,int y){if((x+=y)>=mod)x-=mod;}
 
#define p(S,x) (((S)>>(x))&1)
 
int main(){
    int n;scanf("%d",&n);register int i,j,k;
    for(i=0;i<n;++i)for(j=0;j<n;++j)scanf("%d",&c[i][j]);
    for(i=0;i<n;++i)f[1<<i]=g[1<<i]=1;
    for(i=1;i<(1<<n);++i)h[i]=h[i^(i&-i)]+1;
    for(i=1;i<(1<<n);++i){
        if(h[i]==1)continue;
        else{
            g[i]=1;
            for(j=0;j<n;++j)for(k=j+1;k<n;++k)if(p(i,j)&&p(i,k))
                g[i]=(LL)g[i]*(c[j][k]+1)%mod;
            int ins;
            for(j=0;j<n;++j)if(p(i,j)){ins=j;break;}
            for(int mas=(i-1)&i;mas;mas=(mas-1)&i)if(p(mas,ins))
                inc(f[i],(LL)f[mas]*g[i^mas]%mod);
            f[i]=(g[i]>=f[i])?g[i]-f[i]:g[i]+mod-f[i];
        }
    }
    printf("%d",f[(1<<n)-1]);
    return 0;
}

BZOJ1185:[HNOI2007]最小矩形覆盖 凸包+旋转卡壳+三分

思路:

首先有一个显然的性质:就是最小矩形必定有一条边与凸包的边重合.

然后我们自然就是对于每一条边考虑一下:首先找到距离这条边最远的点.然后从这个点到这条边做一条垂线段,用这条垂线段上下平移来得到两个边界.

于是我用旋转卡壳找最远的点,然后对于两边分别三分.

效率好像还不低.

 

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
 
typedef double f2;
 
static const f2 eps=1e-7;
inline int dcmp(const f2&x){return fabs(x)<eps?0:(x<0?-1:1);}
 
#define N 50010
struct Point{
    f2 x,y;
    Point(){}
    Point(f2 _x,f2 _y):x(_x),y(_y){}
     
    inline void read(){scanf("%lf%lf",&x,&y);}
    inline void print(){printf("%.5lf %.5lf\n",x,y);}
     
    bool operator<(const Point&B)const{return fabs(x-B.x)==0?y<B.y:x<B.x;}
     
    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(x*p,y*p);}
};
 
Point getvec(const Point&v){return Point(-v.y,v.x);}
Point getmid(const Point&a,const Point&b){return Point((a.x+b.x)/2,(a.y+b.y)/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));}
f2 cross(const Point&a,const Point&b){return a.x*b.y-a.y*b.x;}
f2 area(const Point&a,const Point&b,const Point&c){return fabs(cross(a-b,a-c))*.5;}
 
struct Line{
    Point p,v;
    Line(Point p1,Point p2,bool d){p=p1,v=d?p1-p2:p2;}
};
 
Point getlineintersection(const Line&l1,const Line&l2){
    return l1.p+l1.v*(cross(l1.p-l2.p,l2.v)/cross(l1.v,l2.v));
}
 
Point P[N],stk[N<<1];int top;
 
Point move(Point p,Point v,f2 l){
    return p+v*(l/sqrt(v.x*v.x+v.y*v.y));
}
 
f2 res=1e10;
Point re[N];
 
int main(){
    //freopen("tt.in","r",stdin);
    int n;scanf("%d",&n);
    register int i,j;
     
    for(i=1;i<=n;++i)P[i].read();
     
    sort(P+1,P+n+1);
    int ins;for(ins=n;ins!=1&&dcmp(P[ins].x-P[ins-1].x)==0;--ins);
    for(i=ins;i<=(n+ins)/2;++i)swap(P[i],P[n+ins-i]);
     
    for(i=1;i<=n;++i){
        if(!top)stk[++top]=P[i];
        else{while(top>=2&&dcmp(cross(stk[top-1]-stk[top],stk[top]-P[i]))<=0)--top;stk[++top]=P[i];}
    }
    int instk=top;
    for(i=ins-1;i>=1;--i){
        if(top==instk)stk[++top]=P[i];
        else{while(top>=instk+1&&dcmp(cross(stk[top-1]-stk[top],stk[top]-P[i]))<=0)--top;stk[++top]=P[i];}
    }--top;
     
    for(i=top+1;i<=2*top;++i)stk[i]=stk[i-top];
     
    int r=2,L,R,ll,rr;
    f2 h1,h2;
    for(i=1;i<=top;++i){
        while(area(stk[i],stk[i+1],stk[r])<=area(stk[i],stk[i+1],stk[r+1]))++r;
         
        Line l1=Line(stk[i],stk[i+1],1);
        Line l2=Line(stk[r],getvec(l1.v),0);
        Point pr=getlineintersection(l1,l2);
         
        h1=h2=0;
        for(L=i+1,R=r;;){
            if(R-L+1<=5){for(j=L;j<=R;++j)h1=max(h1,area(stk[r],pr,stk[j])*2/getdis(stk[r],pr));break;}
             
            ll=L+(R-L+1)/3,rr=R-(R-L+1)/3;
            if(area(stk[r],pr,stk[ll])<=area(stk[r],pr,stk[rr]))L=ll;else R=rr;
        }
        for(L=r,R=i+top;;){
            if(R-L+1<=5){for(j=L;j<=R;++j)h2=max(h2,area(stk[r],pr,stk[j])*2/getdis(stk[r],pr));break;}
             
            ll=L+(R-L+1)/3,rr=R-(R-L+1)/3;
            if(area(stk[r],pr,stk[ll])<=area(stk[r],pr,stk[rr]))L=ll;else R=rr;
        }
         
        if(getdis(stk[r],pr)*(h1+h2)<res){
            res=getdis(stk[r],pr)*(h1+h2);
            re[0]=move(pr,stk[i]-stk[i+1],h1);
            re[1]=move(stk[r],stk[i]-stk[i+1],h1);
            re[2]=move(stk[r],stk[i+1]-stk[i],h2);
            re[3]=move(pr,stk[i+1]-stk[i],h2);
        }
    }
     
    printf("%.5lf\n",res);
    int bins;Point Minre;
    for(Minre=re[0],bins=0,i=1;i<4;++i)if(dcmp(re[i].y-Minre.y)<0||(dcmp(re[i].y-Minre.y)==0&&dcmp(re[i].x-Minre.x)<0))Minre=re[i],bins=i;
     
    for(i=0;i<4;++i)re[(i+bins)%4].print();
     
    return 0;
}

BZOJ1190:[HNOI2007]梦幻岛宝珠 dp

思路:

首先很显然注意到应该按照2的幂次分组dp.

我的想法乱七八糟,就不提了.

发一个详细的网址:http://blog.csdn.net/PoPoQQQ/article/details/43953609

顺便吐槽我太弱.

 

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
#define N 110
struct cost{
    int a,b;
    inline void init(int x){
        b=0;
        for(;x%2==0;x>>=1)++b;
        a=x;
    }
};
cost c[N];int v[N];
 
long long f[31][1010];
 
#define Max(x,y) ((x)>(y)?(x):(y))
 
int main(){
    int n,w,x;
    register int i,j,k;
    while(scanf("%d%d",&n,&w)&&(n+w>0)){
        memset(f,0,sizeof f);
        for(i=1;i<=n;++i)scanf("%d%d",&x,&v[i]),c[i].init(x);
         
        for(i=1;i<=n;++i)for(j=min(1000,w>>c[i].b);j>=c[i].a;--j)f[c[i].b][j]=Max(f[c[i].b][j],f[c[i].b][j-c[i].a]+v[i]);
         
        for(i=0;i<=30;++i)for(j=1;j<=1000&&j<=(w>>i);++j)f[i][j]=Max(f[i][j],f[i][j-1]);
         
        int re=0;
        for(k=1;k<=30&&(1<<k)<=w;++k){
            for(j=min(1000,w>>k);j>=0;--j)
                for(i=0;i<=j;++i)
                    f[k][j]=Max(f[k][j],f[k][j-i]+f[k-1][min(2*i+((w>>(k-1))&1),1000)]),re=Max(re,f[k][j]);
        }
         
        printf("%d\n",re);
    }
     
    return 0;
}

BZOJ1493:[NOI2007]项链工厂 Splay

思路:

脑补了半天发现还是不知道怎么上线段树,于是无脑Splay.

操作应该都非常容易实现吧.

对于每一个节点记录一下区间的左右端的颜色,自己的颜色以及颜色段数.

没什么好说的,详情见代码.

 

教训:

我得到区间是利用返回Splay中的一个点来实现的.

注意这个点并不是静态的.

所以得到之后立即将信息存下来.

否则再去得到别的区间的时候,这个点的信息会发生变化.

 

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
#define N 500010
 
#define l ch[0]
#define r ch[1]
 
int a[N];
struct Node{
    Node*ch[2],*pa;
    int siz,num,c,col,lcol,rcol;
    bool rev;
    Node():siz(0),num(0),rev(0){}
     
    bool d(){return this==pa->ch[1];}
    inline void sc(Node*p,bool d){ch[d]=p,p->pa=this;}
}mem[N],*P=mem,Tnull,*null=&Tnull,*Root;
 
inline void covit(Node*p,int c){
    p->c=p->col=p->lcol=p->rcol=c;
    p->num=1;
}
inline void revit(Node*p){
    p->rev^=1,swap(p->lcol,p->rcol),swap(p->l,p->r);
}
inline void down(Node*p){
    if(p->rev){
        if(p->l!=null)revit(p->l);
        if(p->r!=null)revit(p->r);
        p->rev=0;
    }
    if(p->c!=0){
        if(p->l!=null)covit(p->l,p->c);
        if(p->r!=null)covit(p->r,p->c);
        p->c=0;
    }
}
inline void up(Node*p){
    p->siz=p->l->siz+1+p->r->siz;
    p->num=p->l->num+p->r->num+1;
    if(p->l!=null&&p->col==p->l->rcol)--p->num;
    if(p->r!=null&&p->col==p->r->lcol)--p->num;
    p->lcol=(p->l!=null)?p->l->lcol:p->col;
    p->rcol=(p->r!=null)?p->r->rcol:p->col;
}
Node*Newnode(int col){
    P->l=P->r=P->pa=null;
    P->siz=1,P->num=1,P->c=P->rev=0,P->col=P->lcol=P->rcol=col;return P++;
}
Node*Build(int tl,int tr){
    if(tl>tr)return null;
    int mid=(tl+tr)>>1;
    Node*q=Newnode(a[mid]);
    q->sc(Build(tl,mid-1),0);
    q->sc(Build(mid+1,tr),1);
    up(q);
    return q;
}
inline void Rot(Node*p){
    bool d=p->d();Node*fa=p->pa;
    down(fa),down(p);
    fa->pa->sc(p,fa->d()),fa->sc(p->ch[!d],d),p->sc(fa,!d);
    up(fa);
    if(fa==Root)Root=p;
}
inline void Splay(Node*p,Node*Anc=null){
    while(p->pa!=Anc){
        if(p->pa->pa==Anc)Rot(p);
        else Rot(p->d()==p->pa->d()?p->pa:p),Rot(p);
    }up(p);
}
inline Node*kth(int k){
    Node*re=Root;
    while(1){
        down(re);
        if(re->l->siz>=k)re=re->l;
        else if(k==re->l->siz+1)return re;
        else k-=re->l->siz+1,re=re->r;
    }
}
inline Node*get(int dl,int dr){
    Node*p=kth(dl);Splay(p);
    Node*q=kth(dr+2);Splay(q,p);
    return q->l;
}
inline Node*del(int dl,int dr){
    Node*p=get(dl,dr);
    p->pa->sc(null,0),up(p->pa),up(p->pa->pa);
    return p;
}
inline void cover(int dl,int dr,int c){
    covit(get(dl,dr),c);
}
inline void reverse(int dl,int dr){
    revit(get(dl,dr));
}
inline void insert(int ins,Node*x){
    Node*p=kth(ins);Splay(p);
    Node*q=kth(ins+1);Splay(q,p);
    q->sc(x,0),up(q),up(p);
}
 
inline void pre(){
    Root=Newnode(0);
    Node*p=Newnode(0);
    Root->sc(p,1),up(Root);
}
 
int main(){
    int n,Maxc;scanf("%d%d",&n,&Maxc);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
     
    pre(),insert(1,Build(1,n));
     
    int m;
    scanf("%d",&m);
    char qte[10];int x,y,c;
    while(m--){
        scanf("%s",qte);
         
        if(qte[0]=='R'){
            scanf("%d",&x);
             
            Node*p=del(n-x+1,n);
            insert(1,p);
        }
         
        if(qte[0]=='F'){
            reverse(2,n);
        }
         
        if(qte[0]=='S'){
            scanf("%d%d",&x,&y);
            if(x!=y){
                 
                if(x>y)swap(x,y);
             
                Node*getx=del(x,x),*gety=del(y-1,y-1);
                insert(x,gety),insert(y,getx);
            }
        }
         
        if(qte[0]=='P'){
            scanf("%d%d%d",&x,&y,&c);
             
            if(x<=y)cover(x,y,c);
            else cover(x,n,c),cover(1,y,c);
        }
         
        if(strlen(qte)==1&&qte[0]=='C'){
            Node*re=get(1,n);
            printf("%d\n",re->num==1?1:re->num-(re->lcol==re->rcol));
        }
         
        if(qte[0]=='C'&&qte[1]=='S'){
            scanf("%d%d",&x,&y);
            if(x<=y){
                Node*re=get(x,y);
                printf("%d\n",re->num);
            }
            else{
                int re=0,rcol,lcol;
                Node*re1=get(x,n);re+=re1->num,rcol=re1->rcol;
                Node*re2=get(1,y);re+=re2->num,lcol=re2->lcol;
                printf("%d\n",re-(rcol==lcol));
            }
        }
    }
     
    return 0;
}

BZOJ1494:[NOI2007]生成树计数 矩阵乘法+最小表示法+插头dp

思路:

从前向后考虑每一个点,不难发现每个点只能向它的前\(k\)个点连边.

因此我们维护一下最后\(k\)个点的连通性,然后做状压dp.

我们用最小表示法来表示\(k\)个点的连通性,比如12123这种形式,相同的数字表示在相同集合中.

然后我们就枚举一下第\(k+1\)个点如何向前\(k\)个点连边.

注意:合法的连边不能出现环,同时还要保证第\(1\)个点不能被孤立.

然后就有了一个转移矩阵,就可以跑矩乘了.

注意每个状态的初始方案并不是一,而是每个每个连通图的所有生成树的方案之积.

而由Prufer序列可得\(n\)个点的生成树总数为\(n^{n-2}\).

至此应该没有什么问题了(除了代码实现).

 

#include<map>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
 
long long n;int k;
 
int lim;
 
int cnt,seq[110][10],now[10],vis[10];
 
void dfs(int dep){
    if(dep==k+1){
        memset(vis,0,sizeof vis);
        for(int i=1;i<=k;++i)if(!vis[now[i]])vis[now[i]]=i;
        for(int i=1;i<=lim;++i)if(!vis[i])return;
        for(int i=1;i<=lim;++i)for(int j=i+1;j<=lim;++j)if(vis[i]>vis[j])return;
         
        ++cnt;for(int i=1;i<=k;++i)seq[cnt][i]=now[i];
        return;
    }
     
    for(int i=1;i<=lim;++i)now[dep]=i,dfs(dep+1);
}
 
#define Min(x,y) ((x)<(y)?(x):(x))
 
inline int calc(int d){
    int mi=1,re=0;
    for(int i=1;i<=k;++i)re+=mi*seq[d][i],mi*=10;
    return re;
}
map<int,int>M;
 
static const int mod=65521;
inline void inc(int&x,int y){if((x+=y)>=mod)x-=mod;}
 
struct Matrix{
    int w,h,d[110][110];
    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);
        for(int i=1;i<=C.w;++i)for(int j=1;j<=C.h;++j)
            for(int k=1;k<=h;++k)inc(C.d[i][j],(long long)d[i][k]*B.d[k][j]%mod);
        *this=C;
    }
};
 
int pre[10],pa[10],nowseq[10];
 
inline int find(int p[],int x){return x==p[x]?x:p[x]=find(p,p[x]);}
inline void merge(int p[],int x,int y){p[find(p,x)]=find(p,y);}
 
const int get[]={1,1,1,3,16,125};
 
int main(){
    scanf("%d%lld",&k,&n),k=Min(k,n-1);register int i,j;
     
    for(i=1;i<=k;++i){
        lim=i,dfs(1);
    }
     
    Matrix ans(1,cnt);
    for(i=1;i<=cnt;++i){
        ans.d[1][i]=1;
        for(j=1;j<=k;++j){int count=0;for(int q=1;q<=k;++q)if(seq[i][q]==j)++count;ans.d[1][i]*=get[count];}
         
        M[calc(i)]=i;
    }
     
    Matrix add(cnt,cnt);
     
    for(int t=1;t<=cnt;++t){
        for(i=1;i<=k+1;++i)pre[i]=i;
        for(i=1;i<=k;++i)for(j=i+1;j<=k;++j)if(seq[t][i]==seq[t][j])merge(pre,i,j);
         
        for(int S=0;S<(1<<k);++S){
            memcpy(pa,pre,sizeof pa);
            bool can=1;
            for(i=0;i<k;++i)if((S>>i)&1){if(find(pa,i+1)==find(pa,k+1))can=0;else merge(pa,i+1,k+1);}
            if(!can)continue;
             
            for(i=1;i<=k+1;++i)pa[i]=find(pa,i);
             
            memset(nowseq,0,sizeof nowseq);
            int id=0;
            for(i=1;i<=k+1;++i){
                if(nowseq[i]==0){
                    ++id;
                    for(j=i;j<=k+1;++j)if(pa[j]==pa[i])nowseq[j]=id;
                }
            }
             
            bool ok=0;
            for(i=2;i<=k+1;++i)if(nowseq[1]==nowseq[i]){ok=1;break;}
             
            if(ok){
                memset(nowseq,0,sizeof nowseq);
                id=0;
                for(i=2;i<=k+1;++i){
                    if(nowseq[i]==0){
                        ++id;
                        for(j=i;j<=k+1;++j)if(pa[j]==pa[i])nowseq[j]=id;
                    }
                }
                 
                int mi=1,re=0;
                for(i=2;i<=k+1;++i)re+=mi*nowseq[i],mi*=10;
                 
                if(M[re]==0)puts("WA");
                inc(add.d[t][M[re]],1);
            }
        }
    }
     
    n-=k;
    for(;n;n>>=1,add*=add)if(n&1)ans*=add;
     
    printf("%d",ans.d[1][1]);
     
    return 0;
}

BZOJ3884: 上帝与集合的正确用法 数论

首先我们知道欧拉定理:

\[a^{\phi(p)}=1(mod~p)((a,p)=1)\]

那么看起来这道题有点能做了.

可惜如果\(p\)是偶数又怎么办?

我们有\(cx%cy=c(x%y)\)这条神奇的性质.也很容易证明.

这样,我们就可以将问题递归处理了.

对于\(\phi\)函数的不断迭代是\(log\)级别的,然后我们每次\(O(\sqrt{n}logn)\)求\(\phi\).因此时间复杂度为\(O(\sqrt{n}logn)\).

还有另外一种不用讨论的方法:

有公式:\(a^x=a^{x\%\phi(p)+\phi(p)}(mod~p)(x>=\phi(p))\)

这样就能更加自然地递归下去了.

 

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
typedef long long LL;
inline int getphi(int x){
    int re=x,t=x;
    for(int i=2;i*i<=x;++i){
        if(t%i==0){
            re=(LL)re*(i-1)/i;
            while(t%i==0)t/=i;
        }
    }if(t!=1)re=(LL)re*(t-1)/t;
    return re;
}
 
inline int ksm(int x,int y,int p){
    int re=1,t=x;for(;y;y>>=1,t=(LL)t*t%p)if(y&1)re=(LL)re*t%p;return re;
}
inline int calc(int p){
    if(p==1)return 0;
    else{
        int phi=getphi(p),re=calc(phi);
        return ksm(2,re+phi,p);
    }
}
 
int main(){
    int T,p;scanf("%d",&T);
    while(T--){
        scanf("%d",&p);
        printf("%d\n",calc(p));
    }
    return 0;
}

BZOJ2219:数论之神 数论

BZOJ3564:[SHOI2014]信号增幅仪 坐标变换+计算几何+最小圆覆盖

思路:

首先我们将所有的点都顺时针旋转\(a\)度,这样就相当于坐标系逆时针旋转了\(a\)度,那么现在椭圆的长轴就与\(x\)轴平行了.

那么还要考虑椭圆现在是横向伸长了\(p\)倍,于是我们就将所有点目前的横坐标缩小\(p\)倍.

然后就是随机增量法的最小圆覆盖了.

 

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
 
typedef double f2;
 
static const f2 eps=1e-7;
inline int dcmp(const f2&x){return fabs(x)<eps?0:(x<0?-1:1);}
 
#define N 50010
 
struct Point{
    f2 x,y;
    Point(){}
    Point(f2 _x,f2 _y):x(_x),y(_y){}
     
    inline void read(){scanf("%lf%lf",&x,&y);}
     
    bool operator<(const Point&B)const{return dcmp(x-B.x)==0?y<B.y:x<B.x;}
     
    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);}
}P[N];
 
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));}
f2 cross(const Point&a,const Point&b){return a.x*b.y-a.y*b.x;}
 
Point getmid(const Point&a,const Point&b){return Point((a.x+b.x)/2,(a.y+b.y)/2);}
Point getvec(const Point&v){return Point(-v.y,v.x);}
Point rot(const Point&p,f2 alp){return Point(p.x*cos(alp)-p.y*sin(alp),p.x*sin(alp)+p.y*cos(alp));}
 
struct Line{
    Point p,v;
    Line(){}
    Line(const Point&p1,const Point&p2):p(p1),v(p1-p2){}
};
Line getvecline(const Point&p1,const Point&p2){
    Line re;
    re.p=getmid(p1,p2),re.v=getvec(p1-p2);
    return re;
}
Point getlineintersection(const Line&l1,const Line&l2){
    return l1.p+l1.v*(cross(l1.p-l2.p,l2.v)/cross(l1.v,l2.v));
}
Point getpoint(Point p1,Point p2,Point p3){
    static Point P[3];P[0]=p1,P[1]=p2,P[2]=p3;sort(P,P+3);p1=P[0],p2=P[1],p3=P[2];
    if(dcmp(cross(p1-p2,p2-p3))==0)return getmid(p1,p3);
    else return getlineintersection(getvecline(p1,p2),getvecline(p2,p3));
}
 
int main(){
    int n;scanf("%d",&n);register int i,j,k;
    for(i=1;i<=n;++i)P[i].read();
     
    f2 alp,p;scanf("%lf%lf",&alp,&p);
     
    for(i=1;i<=n;++i)P[i]=rot(P[i],-alp/180*acos(-1.0)),P[i].x/=p;
     
    for(i=2;i<=n;++i)swap(P[i],P[rand()%i+1]);
    Point o=P[1];f2 r=0;
    for(i=2;i<=n;++i)if(getdis(o,P[i])>r){
        o=P[i],r=0;
        for(j=1;j<i;++j)if(getdis(o,P[j])>r){
            o=getmid(P[i],P[j]),r=getdis(o,P[i]);
            for(k=1;k<j;++k)if(getdis(o,P[k])>r)o=getpoint(P[i],P[j],P[k]),r=getdis(o,P[i]);
        }
    }
     
    printf("%.3lf",r);
     
    return 0;
}

BZOJ2331:[SCOI2011]地板 插头dp

思路:

我们将插头分为两种,一种是还没有拐弯的,另外一种是已经拐弯的.

转移就自己YY一下就行了.

注意边界条件.

 

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
#define R 110
#define C 110
int r,c;
bool Read[R][C],map[R][C];
 
char s[C];
 
static const int mod=20110520;
 
inline void inc(int&x,int y){if((x+=y)>=mod)x-=mod;}
 
struct State{
    int d[11];
    State(){memset(d,0,sizeof d);}
 
    inline int hash(){
        int re=0,mi=1;
        for(int i=0;i<=c;++i)re+=mi*d[i],mi*=3;
        return re;
    }
    inline int get(int ins){return d[ins];}
    inline State change(int ins,int x){State re=*this;re.d[ins]=x;return re;}
    inline void flush(){
        for(int i=c;i>=1;--i)d[i]=d[i-1];d[0]=0;
    }
     
    inline void print(){for(int i=0;i<=c;++i)printf("%d",d[i]);}
};
 
struct HashSet{
    int ins[200010],w[200010],ind;State S[200010];
 
    inline void reset(){memset(ins,-1,sizeof ins),ind=0;}
 
    inline void Insert(State s,int _w){
        //s.print(),printf(" %d\n",_w);
        int hash=s.hash();
        if(ins[hash]==-1)S[ind]=s,w[ind]=_w,ins[hash]=ind++;else inc(w[ins[hash]],_w);
    }
}set[2];
 
int main(){
    scanf("%d%d",&r,&c);register int i,j,k;
    for(i=1;i<=r;++i){
        scanf("%s",s+1);
        for(j=1;j<=c;++j)Read[i][j]=s[j]!='*';
    }
    if(r<c){for(i=1;i<=r;++i)for(j=1;j<=c;++j)map[j][i]=Read[i][j];swap(r,c);}
    else for(i=1;i<=r;++i)for(j=1;j<=c;++j)map[i][j]=Read[i][j];
 
    int now=0,last=1;
 
    State begin;set[now].reset(),set[now].Insert(begin,1);
 
    State nows;int noww,d1,d2;
    for(i=1;i<=r;++i){
        for(j=1;j<=c;++j){
            swap(now,last),set[now].reset();
 
            for(k=0;k<set[last].ind;++k){
                nows=set[last].S[k],noww=set[last].w[k],d1=nows.get(j-1),d2=nows.get(j);
 
                if(!map[i][j]){
                    if(d1==0&&d2==0)set[now].Insert(nows,noww);
                }
                else{
                    if(d1==0&&d2==0){
                        if(i!=r&&j!=c)set[now].Insert(nows.change(j-1,2).change(j,2),noww);
                        if(i!=r)set[now].Insert(nows.change(j-1,1).change(j,0),noww);
                        if(j!=c)set[now].Insert(nows.change(j-1,0).change(j,1),noww);
                    }
                    if(d1==1&&d2==0){
                        if(j!=c)set[now].Insert(nows.change(j-1,0).change(j,1),noww);
                        if(i!=r)set[now].Insert(nows.change(j-1,2).change(j,0),noww);
                    }
                    if(d1==0&&d2==1){
                        if(i!=r)set[now].Insert(nows.change(j-1,1).change(j,0),noww);
                        if(j!=c)set[now].Insert(nows.change(j-1,0).change(j,2),noww);
                    }
                    if(d1==2&&d2==0){
                        if(j!=c)set[now].Insert(nows.change(j-1,0).change(j,2),noww);
                        set[now].Insert(nows.change(j-1,0).change(j,0),noww);
                    }
                    if(d1==0&&d2==2){
                        if(i!=r)set[now].Insert(nows.change(j-1,2).change(j,0),noww);
                        set[now].Insert(nows.change(j-1,0).change(j,0),noww);
                    }
                    if(d1==1&&d2==1)set[now].Insert(nows.change(j-1,0).change(j,0),noww);
                }
            }
        }
 
        swap(now,last),set[now].reset();
        for(k=0;k<set[last].ind;++k){
            nows=set[last].S[k],noww=set[last].w[k],nows.flush();
            set[now].Insert(nows,noww);
        }
 
    }
 
    int res=0;for(k=0;k<set[now].ind;++k)inc(res,set[now].w[k]);
 
    printf("%d",res);
 
    return 0;
}