3
11
2016
0

Diary of March 11th

今天没有模拟赛
又是悠闲的一天

搜索大法好
剪枝保平安
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<ctime>
#include<cstdlib>
#include<string>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define Rep(x,a,b) for (int x=a;x<=b;x++)
#define Drp(x,a,b) for (int x=a;x>=b;x--)
#define Cross(x,a) for (int x=head[a];~x;x=next[x])
#define ll long long
#define oo (1<<29)
#define mk(a,b) make_pair(a,b)
#define fr first
#define sc second
using namespace std;
inline int IN(){
	int x=0,ch=getchar(),f=1;
	while (!isdigit(ch)&&(ch!='-')&&(ch!=EOF)) ch=getchar();
	if (ch=='-'){f=-1;ch=getchar();}
	while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}
inline void Out(ll x){
	if (x<0) putchar('-'),x=-x;
	if (x>=10) Out(x/10),putchar(x%10+'0');
		else putchar(x+'0');
}
ll a[45],s,x,Ans;
int ax;
void DFS(int dig,ll sum){
	if (dig==-1){
		if (sum==s) Ans+=a[ax];
		return;
	}
	if ((x>>dig)&1){
		if (sum+a[dig]<=s) DFS(dig-1,sum+a[dig]);
	}
	else{
		if (sum+a[dig+1]<=s) DFS(dig-1,sum+a[dig+1]);
		if (sum+a[dig+1]-2>=s) DFS(dig-1,sum);
	}
}
int main(){
	a[0]=1;
	Rep(i,1,41) a[i]=a[i-1]<<1;
	scanf("%I64d%I64d",&s,&x);
	if (x==0){
		if (s&1||s<2) puts("0");else puts("1");
		return 0;
	}
	Rep(i,0,40) if ((x>>i)&1) ax++;
	DFS(40,0);
	if (s==x) Ans-=2;
	Out(Ans);
}
先按字典序排一下序
然后把每两个相邻串的lcp都暴力求出来
比较显然的就是$f(i,j)=min\{f(k,k+1)\},其中i≤k<j$
然后就可以区间DP了
$F$ 数组每个节点表示一段区间
处理时找出其中的$min$,递归处理$l\sim min$,$min+1\sim r$
以此类推 主要就是这样了
不懂的可以看代码 很容易懂
复杂度就是$O(n^2)$
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<ctime>
#include<cstdlib>
#include<string>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define Rep(x,a,b) for (int x=a;x<=b;x++)
#define Drp(x,a,b) for (int x=a;x>=b;x--)
#define Cross(x,a) for (int x=head[a];~x;x=next[x])
#define ll long long
#define oo (1<<29)
#define mk(a,b) make_pair(a,b)
#define fr first
#define sc second
using namespace std;
inline int IN(){
	int x=0,ch=getchar(),f=1;
	while (!isdigit(ch)&&(ch!='-')&&(ch!=EOF)) ch=getchar();
	if (ch=='-'){f=-1;ch=getchar();}
	while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}
inline void Out(ll x){
	if (x<0) putchar('-'),x=-x;
	if (x>=10) Out(x/10),putchar(x%10+'0');
		else putchar(x+'0');
}
string s[2005];
char S[505];
int n,k,T;
ll f[2005][2005],pre[2005];
int solve(int l,int r){
	if (l==r) return 0;
	int now=++T,Mn=oo,pos;
	Rep(i,l,r-1) if (pre[i]<Mn) Mn=pre[i],pos=i;
	int L=solve(l,pos),R=solve(pos+1,r);
	Rep(i,0,pos-l+1) Rep(j,0,r-pos)
		f[now][i+j]=max(f[now][i+j],f[L][i]+f[R][j]+1ll*i*j*Mn);
	return now;
}
int main(){
	n=IN(),k=IN();
	Rep(i,1,n){
		scanf("%s",S);
		s[i]=string(S);
	}
	sort(s+1,s+n+1);
	Rep(i,1,n-1){
		int cur=0;
		while (cur<s[i].size()&&cur<s[i+1].size()&&s[i][cur]==s[i+1][cur]) cur++;
		pre[i]=cur;
	}
	Out(f[solve(1,n)][k]);
}
斜率优化DP
首先读入数据存到数组$ A $中,再设一个$ P $数组。
其中$P_i=\sum_{j=1}^{j≤i}{A_j}$ (嘿嘿嘿,就是前缀和)
考虑 $k,j$ 换到位置 $i$ 的贡献 $(k<j<i)$
如果 $j$ 比 $k$ 优,就是
$P_k-P_i+A_k*(i-k)<P_j-P_i+A_j*(i-j)$
移项之后,得到
$P_k-k*A_k-P_j+j*A_j<i*(A_j-A_k)$
然后就可以斜率优化辣!
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<ctime>
#include<cstdlib>
#include<string>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define Rep(x,a,b) for (int x=a;x<=b;x++)
#define Drp(x,a,b) for (int x=a;x>=b;x--)
#define Cross(x,a) for (int x=head[a];~x;x=next[x])
#define ll long long
#define oo (1<<29)
using namespace std;
inline int IN(){
	int x=0,ch=getchar(),f=1;
	while (!isdigit(ch)&&(ch!='-')&&(ch!=EOF)) ch=getchar();
	if (ch=='-'){f=-1;ch=getchar();}
	while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}
inline void Out(ll x){
	if (x<0) putchar('-'),x=-x;
	if (x>=10) Out(x/10),putchar(x%10+'0');
		else putchar(x+'0');
}
const int N=200005;
int h,t,n,q[N];
ll Ans,Max=-oo,a[N],p[N],b[N];
ll Y(int k,int j){return p[k]-a[k]*k-p[j]+a[j]*j;}
ll X(int k,int j){return a[j]-a[k];}
int main(){
	n=IN();
	Rep(i,1,n) a[i]=IN(),b[n-i+1]=a[i],Ans+=a[i]*i;
	Rep(i,1,n) p[i]=p[i-1]+a[i];
	h=t=1;q[1]=1;
	Rep(i,2,n){
		while (h<t&&Y(q[t],i)*X(q[t-1],q[t])<=Y(q[t-1],q[t])*X(q[t],i)) t--;
		q[++t]=i;
		while (h<t&&Y(q[h],q[h+1])<X(q[h],q[h+1])*i) h++;
		Max=max(Max,p[q[h]]-p[i]+a[q[h]]*(i-q[h]));
	}
	Rep(i,1,n) a[i]=b[i];
	Rep(i,1,n) p[i]=p[i-1]+a[i];
	h=t=1;q[1]=1;
	Rep(i,2,n){
		while (h<t&&Y(q[t],i)*X(q[t-1],q[t])<=Y(q[t-1],q[t])*X(q[t],i)) t--;
		q[++t]=i;
		while (h<t&&Y(q[h],q[h+1])<X(q[h],q[h+1])*i) h++;
		Max=max(Max,p[i]-p[q[h]]+a[q[h]]*(q[h]-i));
	}
	Out(Ans+Max);
}
首先把每个l的r处理出来……
怎么处理呢???二分
因为R不下降,C不上升
所以就可以每次二分找到R和C相差最小的
这个可以用ST表 $O(nlog_2^n)$预处理,$O(1)$查询
然后排序一下
某个数产生贡献的概率就是它前面全都不选,
它一定选,然后后面随便选
所以第i个数产生贡献的概率就是$\frac{C_{n-i}^{k-1}}{C_n^k}$
从前往后搞一搞就好了
不懂的 可以看代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<ctime>
#include<cstdlib>
#include<string>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define Rep(x,a,b) for (int x=a;x<=b;x++)
#define Drp(x,a,b) for (int x=a;x>=b;x--)
#define Cross(x,a) for (int x=head[a];~x;x=next[x])
#define ll long long
#define oo (1<<29)
using namespace std;
inline int IN(){
	int x=0,ch=getchar(),f=1;
	while (!isdigit(ch)&&(ch!='-')&&(ch!=EOF)) ch=getchar();
	if (ch=='-'){f=-1;ch=getchar();}
	while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}
inline void Out(ll x){
	if (x<0) putchar('-'),x=-x;
	if (x>=10) Out(x/10),putchar(x%10+'0');
		else putchar(x+'0');
}
const int N=1000005;
int lg[N],pw[25],v[N][25],c[N][25],val[N]; 
int t,h,n,k,sx;
int BnrSac(int l,int r){
	while (l<r){
		int mid=l+r+1>>1,bl=lg[mid-l+1];
		int V=max(v[l][bl],v[mid-pw[bl]+1][bl]);
		int C=min(c[l][bl],c[mid-pw[bl]+1][bl]);
		if (V<=C) l=mid;else r=mid-1;
	}
	return l;
}
int main(){
	n=IN(),k=IN();
	lg[1]=0;Rep(i,2,n) lg[i]=lg[i>>1]+1;
	pw[0]=1;Rep(i,1,lg[n]+1) pw[i]=pw[i-1]<<1;
	Rep(i,1,n) v[i][0]=IN()*100;
	Rep(i,1,n) c[i][0]=IN();
	Rep(j,1,lg[n]+1) Rep(i,1,n-pw[j-1])
		v[i][j]=max(v[i][j-1],v[i+pw[j-1]][j-1]),
		c[i][j]=min(c[i][j-1],c[i+pw[j-1]][j-1]);
	Rep(i,1,n){
		int pos=BnrSac(i,n),bl=lg[pos-i+1];
		int V=max(v[i][bl],v[pos-pw[bl]+1][bl]);
		int C=min(c[i][bl],c[pos-pw[bl]+1][bl]);
		val[i]=min(V,C);
		V=max(V,v[pos+1][0]),C=min(C,c[pos+1][0]);
		val[i]=max(val[i],min(V,C));
	}
	sort(val+1,val+n+1);
	long double Ans=0,nrt=1.0*k/n;
	Rep(i,1,n-k+1){
		Ans+=nrt*val[i];
		nrt=nrt/(n-i)*(n-k+1-i);
	}
	printf("%.10lf\n",(double)Ans);
}

 

Category: Diary | Tags: 搜索 区间DP DP 凸包 二分 概率/期望 | Read Count: 415

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com