BZOJ1122: [POI2008]账本BBB 单调性+贪心

 

BZOJ4123: [Baltic2015]Hacker

BZOJ1704: [Usaco2007 Mar]Face The Right Way 自动转身机

题解:

首先枚举$k$,那我们需要知道给定$k$,最少进行多少次操作.

显然应该从小到大进行扫描,考虑当前位置$i$,若颜色不对,则反转$[i,i+k-1]$.

然后再判定剩下的位置颜色对不对.

这个过程可以利用单调队列轻松实现,详情见代码.

代码:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
#define N 5010
bool a[N];
int q[N],fr,ta;
int main(){
    int n;
    scanf("%d",&n);
    int i,j;
    char s[10];
    for(i=1;i<=n;++i){
        scanf("%s",s);
        a[i]=(s[0]=='F');
    }
    int ans=0x3f3f3f3f,temp,k;
    for(i=1;i<=n;++i){
        fr=ta=0;
        for(temp=0,j=1;j+i-1<=n;++j){
            while(fr!=ta&&q[fr]+i-1<j)
                ++fr;
            if((((ta-fr)&1)^a[j])==0){
                q[ta++]=j;
                ++temp;
            }
        }
        bool ok=1;
        for(j=n+2-i;j<=n;++j){
            while(fr!=ta&&q[fr]+i-1<j)
                ++fr;
            if((((ta-fr)&1)^a[j])==0)
                ok=0;
        }
        if(ok){
            if(temp<ans){
                ans=temp;
                k=i;
            }
        }
    }
    cout<<k<<" "<<ans<<endl;
    return 0;
}

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;
}

APIO2010信号覆盖 单调性+计算几何+容斥原理

思路:

一开始只是YY出了一个\(O(n^3logn)\)的算法.

我们枚举每两个点,同时再枚举另外一个点,就能得到这三个点的圆心前在两个点垂直平分线的位置.

那么现在我们再枚举每个点出现在这种圆中有多少种情况:这个点已经给定时,我们可以考虑圆心在垂直平分线上的可行位置,通过解不等式发现仅仅满足一个限制就是可行的圆心.于是就可以用数据结构维护快速得到有多少个可行的圆心.

然而正解的思路非常巧妙:

考虑每四个点,要么形成一个凸多边形,要么形成一个凹多边形.

由于没有任意四个点在一个圆上,那么对于一个凸多边形对于答案的贡献是2,一个凹多边形对于答案的贡献是1.

考虑如何计算凹多边形的数目.(然后我们也可以知道凸多边形的数目)

我们枚举中间的凹点,那么发现形成凹多边形的另外三个点必然不在凹点的同侧.因此我们就转化为计算总数减去在凹点同侧的三元组数目,这个利用极角序排序以及单调性可以\(O(nlogn)\)得到.

因此就在\(O(n^2logn)\)的时间内做出来了.

#include<cmath>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
typedef long long LL;
typedef double f2;
 
#define N 1510
int x[N],y[N];
 
struct Point{
    int x,y;
    f2 Ang;
    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);}
    bool operator<(const Point&B)const{return Ang<B.Ang;}
    inline void print(){printf("%d %d\n",x,y);}
}P[N],seq[N<<1];int cnt;
inline LL Cross(const Point&a,const Point&b){return(LL)a.x*b.y-(LL)a.y*b.x;}
 
int main(){
    //freopen("tt.in","r",stdin);
    int n;scanf("%d",&n);int i,j,k;
    for(i=1;i<=n;++i)P[i].read();
     
    LL res=0;
    for(i=1;i<=n;++i){
        for(cnt=0,j=1;j<=n;++j)if(i^j)seq[++cnt]=P[j],seq[cnt].Ang=atan2(P[j].y-P[i].y,P[j].x-P[i].x);
        sort(seq+1,seq+cnt+1);
        for(j=cnt+1;j<=2*cnt;++j)seq[j]=seq[j-cnt];
         
        res+=(LL)(n-1)*(n-2)*(n-3)/6;
        int get;k=1;
        for(j=1;j<=cnt;++j){
            while(k+1!=j+cnt&&Cross(seq[j]-P[i],P[i]-seq[k+1])<=0)++k;
            //printf("j=%d ",j);seq[j].print();printf("k=%d ",k),seq[k].print();puts("");
            get=k-j;
            res-=(LL)get*(get-1)/2;
        }
    }
     
    LL all=(LL)n*(n-1)*(n-2)*(n-3)/24;
    LL ret=res+(all-res)*2;
    printf("%.3lf",3.0+(f2)ret*6/n/(n-1)/(n-2));
     
    return 0;
}

BZOJ2276:[Poi2011]Temperature 单调性+线段树

思路:

貌似存在\(O(n)\)的算法,不过由于我太弱只想到了\(O(nlogn)\).

现在很困就发代码了...有问题回复找我吧.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
#define N 1000010
struct SegmentTree{
    int a[2100000],M;
    inline void Init(int _siz){
        for(M=1;M<(_siz+2);M<<=1);
        memset(a,0xc0,sizeof a);
    }
    inline void upd(int x,int to){
        for(a[x+=M]=to,x>>=1;x;x>>=1)a[x]=max(a[x<<1],a[x<<1^1]);
    }
    inline int ask(int tl,int tr){
        int res=-1<<30;
        for(tl+=M-1,tr+=M+1;tl^tr^1;tl>>=1,tr>>=1){
            if(~tl&1)res=max(res,a[tl^1]);
            if(tr&1)res=max(res,a[tr^1]);
        }return res;
    }
}Seg;
 
int up[N];
 
namespace Fio{
    inline int getc(){
#ifndef ONLINE_JUDGE
        static const int L=1<<1;
#else
        static const int L=1<<15;
#endif
        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=0;while(!digit(c=getc())&&c!='-');bool sign=c=='-';x=sign?0:c-'0';
        while(digit(c=getc()))x=(x<<1)+(x<<3)+c-'0';if(sign)x=-x;
    }
}
 
int main(){
    int n;Fio::Get(n);register int i,j;
    Seg.Init(n);
    int x;for(i=1;i<=n;++i)Fio::Get(x),Fio::Get(up[i]),Seg.upd(i,x);
     
    int nowmax=Seg.ask(1,1);
    int lans,ans;
    for(lans=2;lans<=n;++lans){
        if(nowmax>up[lans])break;
        nowmax=max(nowmax,Seg.ask(lans,lans));
    }ans=--lans;
     
    for(i=2;i<=n;++i){
        nowmax=Seg.ask(i,lans);
        for(++lans;lans<=n;++lans){
            if(nowmax>up[lans])break;
            nowmax=max(nowmax,Seg.ask(lans,lans));
        }--lans;
        ans=max(ans,lans-i+1);
    }
     
    printf("%d",ans);
     
    return 0;
}

 

POJ3415 Common Substrings 后缀数组+单调栈

思路:

最近好像是被<囚人的旋律>里的主人公的心情所影响,所以效率很低.

例如,这样一道傻逼题我居然傻逼了3个小时!

我整个人都单调栈了.

 

就是求个height,然后在连续块内,我们对于每个属于串A的后缀分别计算出在他前面的属于串B的后缀的影响,在计算出在他之后的属于B的后缀的影响.

然后我们从前到后,再从后到前扫描一边,利用一个单调栈维护一下属于串B的后缀即可.

说起来简单...不过写起来...一定是我太鶸了.

 

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

typedef long long LL;

#define N 200010
char s1[N],s2[N],s[N];int v[N],tv[N],q[N],c[N],sa[N];
inline bool cmp(int x,int y,int hl,int n){
	return v[x]==v[y]&&((x+hl>=n&&y+hl>=n)||(x+hl<n&&y+hl<n&&v[x+hl]==v[y+hl]));
}
inline void calcsa(char*s,int n,int m,int*sa){
	register int i,j,k;int id,hl;
	for(i=0;i<m;++i)c[i]=0;
	for(i=0;i<n;++i)++c[v[i]=s[i]];
	for(i=1;i<m;++i)c[i]+=c[i-1];
	for(i=n-1;i>=0;--i)sa[--c[v[i]]]=i;
	for(int d=1;;++d){
		for(hl=1<<(d-1),id=0,i=n-hl;i<n;++i)q[id++]=i;
		for(i=0;i<n;++i)if(sa[i]>=hl)q[id++]=sa[i]-hl;
		for(i=0;i<m;++i)c[i]=0;
		for(i=0;i<n;++i)++c[v[q[i]]];
		for(i=1;i<m;++i)c[i]+=c[i-1];
		for(i=n-1;i>=0;--i)sa[--c[v[q[i]]]]=q[i];
		for(i=m=0;i<n;i=j+1,++m){
			for(j=i;j<n-1&&cmp(sa[j],sa[j+1],hl,n);++j);
			for(k=i;k<=j;++k)tv[sa[k]]=m;
		}for(i=0;i<n;++i)v[i]=tv[i];
		if(m==n)break;
	}
}
int rk[N],h[N];
inline void calch(char*s,int n,int*sa,int*rk,int*h){
	register int i,j;
	for(i=0;i<n;++i)rk[sa[i]]=i;
	for(i=0;i<n;++i)if(rk[i]){
		j=max(0,h[rk[i-1]]-1);while(i+j<n&&sa[rk[i]-1]+j<n&&s[i+j]==s[sa[rk[i]-1]+j])++j;h[rk[i]]=j;
	}
}

int lim;

struct Case{
	int v,c;
	Case(){}
	Case(int _v,int _c):v(_v),c(_c){}
}stk[N];
int top,nowcnt;long long sum[N];int cnt[N];

int belong[N];
int main(){
	int i,j,k;
	while(scanf("%d",&lim)&&lim){
		scanf("%s%s",s1,s2);int len1=strlen(s1),len2=strlen(s2);
		memset(s,0,sizeof s);int len=0;for(i=0;i<len1;++i)belong[len]=1,s[len++]=s1[i];s[len++]='$';for(i=0;i<len2;++i)belong[len]=2,s[len++]=s2[i];
		calcsa(s,len,256,sa),calch(s,len,sa,rk,h);

		LL ans=0;int tmp;
		for(i=1;i<len;){
			if(h[i]>=lim){
				for(j=i;j<len&&h[j]>=lim;++j);
				for(top=0,h[i-1]=h[i],tmp=h[j],h[j]=h[j-1],k=i-1;k<j;++k){
					nowcnt=0;
					while(top&&stk[top].v>=h[k])nowcnt+=stk[top--].c;
					if(nowcnt)stk[++top]=Case(h[k],nowcnt),sum[top]=sum[top-1]+(LL)stk[top].c*stk[top].v,cnt[top]=cnt[top-1]+stk[top].c;
					if(belong[sa[k]]==2)ans+=sum[top]-(LL)cnt[top]*(lim-1);
					else{
						nowcnt=1;while(top&&stk[top].v>=h[k+1])nowcnt+=stk[top--].c;
						stk[++top]=Case(h[k+1],nowcnt),sum[top]=sum[top-1]+(LL)stk[top].c*stk[top].v,cnt[top]=cnt[top-1]+stk[top].c;
					}
					
				}
				for(top=0,k=j-1;k>=i-1;--k){
					nowcnt=0;
					while(top&&stk[top].v>=h[k+1])nowcnt+=stk[top--].c;
					if(nowcnt)stk[++top]=Case(h[k+1],nowcnt),sum[top]=sum[top-1]+(LL)stk[top].c*stk[top].v,cnt[top]=cnt[top-1]+stk[top].c;
					if(belong[sa[k]]==2)ans+=sum[top]-(LL)cnt[top]*(lim-1);
					else{
						nowcnt=1;while(top&&stk[top].v>=h[k])nowcnt+=stk[top--].c;
						stk[++top]=Case(h[k],nowcnt),sum[top]=sum[top-1]+(LL)stk[top].c*stk[top].v,cnt[top]=cnt[top-1]+stk[top].c;
					}
				}h[j]=tmp;
				i=j;
			}else++i;
		}
		cout<<ans<<endl;
	}
	return 0;
}

BZOJ1492:[NOI2007]货币兑换Cash 斜率优化+cdq分治

 

BZOJ3238: [Ahoi2013]差异 后缀数组+单调队列