近期刷题小结9.24-10.11

NOIP2016提高组换教室

洛谷P1850

题意

两个序列 c c c, d d d,你需依次走过这 n n n个点,如果申请,则有 p p pi 的机会将 c c ci 换成 d d di,但你只能申请 m m m次,求走过这 n n n个点的路径总和的期望最小值。

方法

期望 D P DP DP

f [ i ] [ j ] [ 0 / 1 ] f[i][j][0/1] f[i][j][0/1]表示走过前 i i i个点,当前点是否申请的期望

f [ i ] [ j ] [ 0 ] = m i n ( f [ i − 1 ] [ j ] [ 0 ] + d i s [ c [ i − 1 ] ] [ c [ i ] ] , f [ i − 1 ] [ j ] [ 1 ] + p [ i − 1 ] ∗ d i s [ d [ i − 1 ] ] [ c [ i ] ] + ( 1 − p [ i − 1 ] ) ∗ d i s [ c [ i − 1 ] ] [ c [ i ] ] ; f[i][j][0]=min(f[i-1][j][0]+dis[c[i-1]][c[i]],f[i-1][j][1]+p[i-1]*dis[d[i-1]][c[i]]+(1-p[i-1])*dis[c[i-1]][c[i]]; f[i][j][0]=min(f[i1][j][0]+dis[c[i1]][c[i]],f[i1][j][1]+p[i1]dis[d[i1]][c[i]]+(1p[i1])dis[c[i1]][c[i]];

f [ i ] [ j ] [ 1 ] = m i n ( f [ i − 1 ] [ j − 1 ] [ 0 ] + d i s [ c [ i − 1 ] ] [ c [ i ] ] ∗ ( 1 − p [ i ] ) + d i s [ c [ i − 1 ] ] [ d [ i ] ] ∗ p [ i ] , f [ i − 1 ] [ j − 1 ] [ 1 ] + d i s [ c [ i − 1 ] [ c [ i ] ] ∗ ( 1 − p [ i − 1 ] ) ∗ ( 1 − p [ i ] ) + d i s [ c [ i − 1 ] ] [ d [ i ] ] ∗ ( 1 − p [ i − 1 ] ) ∗ p [ i ] + d i s [ d [ i − 1 ] ] [ c [ i ] ∗ p [ i − 1 ] ∗ ( 1 − p [ i ] ) + d i s [ d [ i − 1 ] ] [ d [ i ] ] ∗ p [ i − 1 ] ∗ p [ i ] ; f[i][j][1]=min(f[i-1][j-1][0]+dis[c[i-1]][c[i]]*(1-p[i])+dis[c[i-1]][d[i]]*p[i],f[i-1][j-1][1]+dis[c[i-1][c[i]]*(1-p[i-1])*(1-p[i])+dis[c[i-1]][d[i]]*(1-p[i-1])*p[i]+dis[d[i-1]][c[i]*p[i-1]*(1-p[i])+dis[d[i-1]][d[i]]*p[i-1]*p[i]; f[i][j][1]=min(f[i1][j1][0]+dis[c[i1]][c[i]](1p[i])+dis[c[i1]][d[i]]p[i],f[i1][j1][1]+dis[c[i1][c[i]](1p[i1])(1p[i])+dis[c[i1]][d[i]](1p[i1])p[i]+dis[d[i1]][c[i]p[i1](1p[i])+dis[d[i1]][d[i]]p[i1]p[i];

??

如果写成
f [ i ] [ j ] [ 1 ] = m i n ( f [ i − 1 ] [ j − 1 ] [ 1 ] + p [ i − 1 ] ∗ d i s [ d [ i − 1 ] ] [ d [ i ] ] + ( 1 − p [ i − 1 ] ) ∗ d i s [ c [ i − 1 ] ] [ d [ i ] ] , f [ i − 1 ] [ j − 1 ] [ 0 ] + d i s [ c [ i − 1 ] ] [ d [ i ] ] ) ∗ p [ i ] + ( 1 − p [ i ] ) ∗ m i n ( f [ i − 1 ] [ j − 1 ] [ 0 ] + d i s [ c [ i − 1 ] ] [ c [ i ] ] , f [ i − 1 ] [ j − 1 ] [ 1 ] + p [ i − 1 ] ∗ d i s [ d [ i − 1 ] ] [ c [ i ] ] + ( 1 − p [ i − 1 ] ) ∗ d i s [ c [ i − 1 ] ] [ c [ i ] ] ) ; f[i][j][1]=min(f[i-1][j-1][1]+p[i-1]*dis[d[i-1]][d[i]]+(1-p[i-1])*dis[c[i-1]][d[i]],f[i-1][j-1][0]+dis[c[i-1]][d[i]])*p[i]+(1-p[i])*min(f[i-1][j-1][0]+dis[c[i-1]][c[i]],f[i-1][j-1][1]+p[i-1]*dis[d[i-1]][c[i]]+(1-p[i-1])*dis[c[i-1]][c[i]]); f[i][j][1]=min(f[i1][j1][1]+p[i1]dis[d[i1]][d[i]]+(1p[i1])dis[c[i1]][d[i]],f[i1][j1][0]+dis[c[i1]][d[i]])p[i]+(1p[i])min(f[i1][j1][0]+dis[c[i1]][c[i]],f[i1][j1][1]+p[i1]dis[d[i1]][c[i]]+(1p[i1])dis[c[i1]][c[i]]);
会精度丢失,WA3个点

心得

终于会期望了,不过对于 d o u b l e double double精度丢失很懵逼

f f f数组一定要清零,以免用到一些不存在的状态!!!

代码
#include
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(x=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int V=304,N=2004,inf=0x3f3f3f3f;
int n,m,v,e,dis[V][V],c[N],d[N];
double p[N],f[N][N][2],ans=inf;
int main(){
	n=read();m=read();v=read();e=read();
	for(int i=1;i<=n;i++)c[i]=read();
	for(int i=1;i<=n;i++)d[i]=read();
	for(int i=1;i<=n;i++)scanf("%lf",&p[i]);
	memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=v;i++)dis[i][i]=0;
	for(int i=1,u,x,w;i<=e;i++){
		u=read();x=read();w=read();
		dis[u][x]=dis[x][u]=min(w,dis[u][x]);
	}
	for(int k=1;k<=v;k++)
		for(int i=1;i<=v;i++)
			for(int j=1;j<=v;j++)
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
	for(int i=0;i<=n;i++)
		for(int j=0;j<=m;j++)
			f[i][j][0]=f[i][j][1]=inf;
	f[1][0][0]=f[1][1][1]=0;
	for(int i=2;i<=n;i++){
		f[i][0][0]=f[i-1][0][0]+dis[c[i-1]][c[i]];
		for(int j=1;j<=i&&j<=m;j++){
			f[i][j][0]=min(f[i-1][j][1]+p[i-1]*dis[d[i-1]][c[i]]+(1-p[i-1])*dis[c[i-1]][c[i]],f[i-1][j][0]+dis[c[i-1]][c[i]]);
			f[i][j][1]=min(f[i-1][j-1][1]+p[i]*p[i-1]*dis[d[i-1]][d[i]]+p[i]*(1-p[i-1])*dis[c[i-1]][d[i]]+(1-p[i])*p[i-1]*dis[d[i-1]][c[i]]+(1-p[i])*(1-p[i-1])*dis[c[i-1]][c[i]],f[i-1][j-1][0]+p[i]*dis[c[i-1]][d[i]]+(1-p[i])*dis[c[i-1]][c[i]]);
			//精度 
		}
	}  
	for(int i=0;i<=m;i++)
		ans=min(ans,min(f[n][i][0],i>0?f[n][i][1]:inf));
	printf("%.2lf",ans);
	return (0-0);
}

文艺平衡树(splay)

洛谷P3391

题意

区间翻转,输出最终区间

方法

s p l a y splay splay
下标建树,对于翻转的区间,通过 s p l a y splay splay形成一棵子树,打翻转标记, p u s h d o w n pushdown pushdown时交换左右儿子,只有在 f i n d find find时才 p u s h d o w n pushdown pushdown?

代码
#include
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=1e5+4;
int n,m,fa[N],ch[N][2],siz[N],rev[N],rt;
inline void pushup(int x){
	siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
}
inline bool getson(int p){
	return ch[fa[p]][1]==p;
}
inline void pushdown(int p){
	if(!rev[p])return;
	swap(ch[p][0],ch[p][1]);
	rev[ch[p][0]]^=1;rev[ch[p][1]]^=1;
	rev[p]^=1;
}
inline void rotate(int p){
	int f=fa[p],g=fa[f],r=getson(p);
	if(f!=rt)ch[g][getson(f)]=p;fa[p]=g;
	ch[f][r]=ch[p][r^1];if(ch[f][r])fa[ch[f][r]]=f;
	ch[p][r^1]=f;fa[f]=p;
	pushup(f);pushup(p);
}
inline void splay(int p,int k){
	for(;fa[p]!=k;rotate(p)){
		if(fa[fa[p]]!=k)rotate(getson(p)==getson(fa[p])?fa[p]:p);
	}
	if(!k)rt=p;
}
inline void build(int l,int r,int f){
	if(l>r)return;
	int mid=(l+r)>>1;
	if(mid<f)ch[f][0]=mid;
	else ch[f][1]=mid;
	fa[mid]=f;siz[mid]=1;//
	if(l==r)return;
	build(l,mid-1,mid);build(mid+1,r,mid);
	pushup(mid);
}
inline int find(int x,int k){
	pushdown(x);
	int s=siz[ch[x][0]];
	if(k==s+1)return x;
	if(k<=s)return find(ch[x][0],k);
	return find(ch[x][1],k-s-1);
}
inline void rever(int l,int r){//l+1->r+1 
	int x=find(rt,l),y=find(rt,r+2);
	splay(x,0);splay(y,x);
	rev[ch[y][0]]^=1;
}
int main(){
	n=read();m=read();
	rt=(n+3)/2;build(1,n+2,0);//
	for(int i=1,l,r;i<=m;i++){
		l=read();r=read();
		rever(l,r);
	}
	for(int i=2;i<=n+1;i++)printf("%d ",find(rt,i)-1);
	return 0;
}

NOI2003文本编辑器

洛谷P4008

题意

支持操作
近期刷题小结9.24-10.11_第1张图片

方法

s p l a y splay splay
每次插入把根节点的右儿子的左儿子弄空,直接在上面建树
每次删除操作,把要删除的点转到一棵子树上,直接删除

心得

没几次默写 s p l a y splay splay,写挂了,调不出来
写代码时一定要认真,写错一个半天都调不出来!!!
变量一定要附初值!!!

代码
#include
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(x=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=2e6+4;
int ch[N][2],fa[N],siz[N],rt,a[N],cnt=0;
char c[N],s[N];
inline int pushup(int p){
	siz[p]=siz[ch[p][1]]+siz[ch[p][0]]+1;
}
inline bool getson(int p){
	return ch[fa[p]][1]==p;
}
inline void rotate(int p){
	int f=fa[p],g=fa[f],r=getson(p);
	if(f!=rt)ch[g][getson(f)]=p;fa[p]=g;//
	ch[f][r]=ch[p][r^1];if(ch[f][r])fa[ch[f][r]]=f;
	ch[p][r^1]=f;fa[f]=p;
	pushup(f);pushup(p);
}
inline void splay(int x,int y){
	for(;fa[x]!=y;rotate(x)){
		if(fa[fa[x]]!=y)rotate(getson(x)==getson(fa[x])?fa[x]:x);
//		printf("%d %d %d\n",x,fa[x],y);
	}
	if(!y)rt=x;
}
inline int find(int p,int pos){
	if(siz[ch[p][0]]+1==pos)return p;
	if(pos<=siz[ch[p][0]])return find(ch[p][0],pos);
	return find(ch[p][1],pos-siz[ch[p][0]]-1);
}
inline void build(int l,int r,int f,int fn,char *s){
	if(l>r)return;
	int mid=(l+r)>>1,t;
	++cnt;t=cnt;
	if(l!=r){build(l,mid-1,t,0,s);build(mid+1,r,t,1,s);}
	a[t]=s[mid];fa[t]=f;ch[f][fn]=t;pushup(t);
}
inline void print(int p){
	if(ch[p][0])print(ch[p][0]);
	putchar(a[p]);
	if(ch[p][1])print(ch[p][1]);
}
int main(){
	char chh[20];
	int T,x,y,l,r,gb=0,len,n;
    chh[0]=chh[1]=chh[2]=a[0]=' ';
	build(1,2,0,0,chh);
	rt=1;
	T=read();
	while(T--){
		scanf("%s",chh);
		if(chh[0]=='M')gb=read();
		if(chh[0]=='I'){
			len=read();
			n+=len;
			s[0]=' ';
			for(int i=1;i<=len;i++){
				s[i]=getchar();
				if(s[i]=='\n'||s[i]=='\r')--i;
			}
			x=find(rt,gb+1);y=find(rt,gb+2);
			splay(x,0);splay(y,x);
			build(1,len,y,0,s);
		}
		if(chh[0]=='D'){
			len=read();len=min(len,n-gb);n-=len;
			x=find(rt,gb+1);y=find(rt,gb+len+2);
			splay(x,0);splay(y,x);ch[y][0]=0;
		}
		if(chh[0]=='G'){
			len=read();
			len=min(len,n-gb);
			x=find(rt,gb+1),y=find(rt,gb+len+2);
			splay(x,0);splay(y,x);print(ch[y][0]);puts("");
		}
		if(chh[0]=='P')--gb;
		if(chh[0]=='N')++gb;
	}
	return (0-0);
}

未调试出的代码

#include
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(x=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=2e6+4;
char c[10];
string s;
int ch[N][2],siz[N],a[N],fa[N],cnt=0,rt=0;
inline int newnode(int x){
	a[++cnt]=x;siz[cnt]=1;
	return cnt;
}
inline void pushnow(int p){
	siz[p]=siz[ch[p][1]]+siz[ch[p][0]]+1;
}
inline bool getson(int p){
	return ch[fa[p]][1]==p;
}
inline void rotate(int p){
	int f=fa[p],g=fa[f],r=getson(p);
	if(f!=rt)ch[g][getson(f)]=p;fa[p]=g;//
	ch[f][r]=ch[p][r^1];if(ch[f][r])fa[ch[f][r]]=f;
	ch[p][r^1]=f;fa[f]=p;
	pushnow(f);pushnow(p);
}
inline void splay(int x,int y){
	for(;fa[x]!=y;rotate(x))
		if(fa[fa[x]]!=y)rotate(getson(x)==getson(fa[x])?fa[x]:x);
	if(!y)rt=x;
}
inline int find(int p,int pos){
	printf("%d %d %d %d %d\n",p,siz[ch[p][0]],pos,ch[28][0],ch[33][0]);
	cout<<(char)a[p]<<endl;
	if(pos<=siz[ch[p][0]])return find(ch[p][0],pos);
	if(pos==siz[ch[p][0]]+1)return p;
	return find(ch[p][1],pos-siz[ch[p][0]]-1);
}
inline void delet(int pos){
	pos=find(rt,pos);
	splay(pos,0);
	if(!ch[pos][0]){
		rt=ch[pos][1];
		fa[rt]=ch[pos][1]=0;
		pushnow(rt);
		return;
	}
	if(!ch[pos][1]){
		rt=ch[pos][0];
		fa[rt]=ch[pos][0]=0;
		pushnow(rt);
		return;
	}
	int x=find(rt,siz[ch[rt][0]]);
	splay(x,rt);
	ch[x][1]=ch[rt][1];if(ch[x][1])fa[ch[x][1]]=x;
	ch[rt][1]=ch[rt][0]=fa[x]=0;rt=x;
	pushnow(x);
}
inline void insert(int &p,int pos,int x){
	if(!p)p=newnode(x);
	else if(pos<=siz[ch[p][0]]){
		insert(ch[p][0],pos,x);
		fa[ch[p][0]]=p;//
	}
	else if(pos>siz[ch[p][0]]+1){
		insert(ch[p][1],pos-siz[ch[p][0]]-1,x);//
		fa[ch[p][1]]=p;//
	}
	else{
		int r=p;
		p=newnode(x);
		fa[p]=fa[r];if(fa[p])ch[fa[p]][getson(r)]=p;
		ch[p][1]=r;fa[r]=p;
		ch[p][0]=ch[r][0];if(ch[p][0])fa[ch[p][0]]=p;ch[r][0]=0;
		if(r==rt)rt=p;
		pushnow(r);
	}
	pushnow(p);
}
int main(){
	freopen("1.in","r",stdin);
//	ios::sync_with_stdio(false);
//	cin.tie(0); 
	int T=read(),x,gb=0,n=0;//哪个字符后 
	while(T--){
		scanf("%s",c);
		switch(c[0]){
			case 'M':gb=read();break;
			case 'I':{
				x=read();
				n+=x;
				int len,all=0;
				while(all<x){
					getline(cin,s);
					len=s.size();
					for(int i=0;i<len;i++){
						insert(rt,gb+1+all+i,s[i]);
						splay(cnt,0);
					}
					all+=len;
				}
				break;
			}
			case 'D':{
				x=read();
				for(int i=1;i<=x;i++,n--){
					if(gb==n)break;
					delet(gb+1);
				}
				break;
			}
			case 'G':{
				x=read();
				for(int i=1;i<=x;i++){
					cout<<i<<endl;
					putchar((char)a[find(rt,gb+i)]);
					
				}
				putchar('\n');
				break;
			}
			case 'P':if(gb)gb--;break;
			case 'N':if(gb<n)gb++;break;
		}
	}
	return (0-0);
}

[AHOI2006]文本编辑器

洛谷P4567

题意

支持操作
近期刷题小结9.24-10.11_第2张图片
读入的时候直接 g e t c h a r getchar getchar即可
注: G E T GET GET操作如果得到的是’\n’,那么输出’\n’后无需再输出一个换行符

方法

s p l a y splay splay
此题比上一道多了区间翻转,像文艺平衡书一样翻转即可

心得

Y e a h ! Yeah! Yeah!

代码
#include
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(x=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=2e6+4;
int ch[N][2],fa[N],siz[N],rev[N],rt,a[N],cnt=0;
char c[N],s[N];
inline void pushdown(int p){
	if(!rev[p])return;
	swap(ch[p][0],ch[p][1]);
	rev[ch[p][0]]^=1;rev[ch[p][1]]^=1;
	rev[p]^=1;
}
inline void pushup(int p){
	siz[p]=siz[ch[p][1]]+siz[ch[p][0]]+1;
}
inline bool getson(int p){
	return ch[fa[p]][1]==p;
}
inline void rotate(int p){
	int f=fa[p],g=fa[f],r=getson(p);
	if(f!=rt)ch[g][getson(f)]=p;fa[p]=g;//
	ch[f][r]=ch[p][r^1];if(ch[f][r])fa[ch[f][r]]=f;
	ch[p][r^1]=f;fa[f]=p;
	pushup(f);pushup(p);
}
inline void splay(int x,int y){
	for(;fa[x]!=y;rotate(x))
		if(fa[fa[x]]!=y)rotate(getson(x)==getson(fa[x])?fa[x]:x);
	if(!y)rt=x;
}
inline int find(int p,int pos){
	pushdown(p);
	if(siz[ch[p][0]]+1==pos)return p;
	if(pos<=siz[ch[p][0]])return find(ch[p][0],pos);
	return find(ch[p][1],pos-siz[ch[p][0]]-1);
}
inline void build(int l,int r,int f,int fn,char *s){
	if(l>r)return;
	int mid=(l+r)>>1,t;
	++cnt;t=cnt;
	if(l!=r){build(l,mid-1,t,0,s);build(mid+1,r,t,1,s);}
	a[t]=s[mid];fa[t]=f;ch[f][fn]=t;pushup(t);
}
int main(){
	char chh[20];
	int T,x,y,gb=0,len,n=0;
    chh[0]=chh[1]=chh[2]=a[0]=' ';
	build(1,2,0,0,chh);
	rt=1;
	T=read();
	while(T--){
		scanf("%s",chh);
		if(chh[0]=='M')gb=read();
		if(chh[0]=='I'){
			len=read();
			n+=len;
			s[0]=' ';
			for(int i=1;i<=len;i++)s[i]=getchar();
			x=find(rt,gb+1);y=find(rt,gb+2);
			splay(x,0);splay(y,x);
			build(1,len,y,0,s);
		}
		if(chh[0]=='D'){
			len=read();len=min(len,n-gb);n-=len;
			x=find(rt,gb+1);y=find(rt,gb+len+2);
			splay(x,0);splay(y,x);ch[y][0]=0;
		}
		if(chh[0]=='G'){
			x=find(rt,gb+1),y=find(rt,gb+3);
			splay(x,0);splay(y,x);putchar(a[ch[y][0]]);
			if(a[ch[y][0]]!='\n'&&a[ch[y][0]]!='\r')puts("");
		}
		if(chh[0]=='P')--gb;
		if(chh[0]=='N')++gb;
		if(chh[0]=='R'){
			len=read();len=min(len,n-gb);
			x=find(rt,gb+1);y=find(rt,gb+len+2);
			splay(x,0);splay(y,x);
			rev[ch[y][0]]^=1;
		}
	}
	return (0-0);
}

[SHOI2002]百事世界杯之旅

洛谷P1291

题意

n n n个不同的数,每个数出现的概率相同,平均需要几次才能凑齐所有的数
用分数输出答案

方法

数学期望
f [ k ] f[k] f[k]表示还剩 k k k个数没有的次数期望
f [ k ] = n − k n f [ k ] + k n f [ k − 1 ] + 1 f[k]=\frac{n-k}{n}f[k]+\frac{k}{n}f[k-1]+1 f[k]=nnkf[k]+nkf[k1]+1
f [ k ] = f [ k − 1 ] + n k f[k]=f[k-1]+\frac{n}{k} f[k]=f[k1]+kn

心得

期望入门题, t c l tcl tcl
l o n g l o n g longlong longlong l l d lld lld输出

代码
#include
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
#define int long long
inline int gcd(int x,int y){
	return y==0?x:gcd(y,x%y);
}
struct node{
	int x,y;
	inline void add(int e,int f){
		x=x*f+y*e;y=y*f;
		e=gcd(x,y);
		x/=e;y/=e;
	}
}ans;
int a,b,c,n;
inline int wei(int x){
	int ret=0;
	while(x){
		x/=10;
		ret++;
	}
	return ret;
}
signed main(){
	n=read();
	ans.x=0;ans.y=1;
	for(int i=1;i<=n;i++)
		ans.add(n,i);
	a=ans.x/ans.y;
	b=ans.x%ans.y;
	c=ans.y;
	if(!b){
		printf("%lld",a);
		return (0-0);
	}
	for(int i=1;i<=wei(a);i++)
		printf(" ");
	printf("%lld\n",b);
	printf("%lld",a);
	for(int i=1;i<=max(wei(b),wei(c));i++)
		printf("-");
	printf("\n");
	for(int i=1;i<=wei(a);i++)
		printf(" ");
	printf("%lld",c);
	return (0-0);
}

OSU!

洛谷P1654

题意

n n n个数的序列,每次有 p [ i ] p[i] p[i]的概率为1,否则为0,连续 x x x个1的贡献为 x x x3,求期望分数。

方法

高次期望
先考虑一次的情况
a [ i ] = ( a [ i − 1 ] + 1 ) ∗ p [ i ] a[i]=(a[i-1]+1)*p[i] a[i]=(a[i1]+1)p[i]
二次
b [ i ] = ( a [ i − 1 ] + 1 ) 2 ∗ p [ i ] b[i]=(a[i-1]+1)^2*p[i] b[i]=(a[i1]+1)2p[i]
b [ i ] = ( b [ i − 1 ] + 2 ∗ a [ i − 1 ] + 1 ) ∗ p [ i ] b[i]=(b[i-1]+2*a[i-1]+1)*p[i] b[i]=(b[i1]+2a[i1]+1)p[i]
三次
f [ i ] = ( a [ i − 1 ] + 1 ) 3 ∗ p [ i ] f[i]=(a[i-1]+1)^3*p[i] f[i]=(a[i1]+1)3p[i]
f [ i ] = ( f [ i − 1 ] + 3 ∗ b [ i − 1 ] + 3 ∗ a [ i − 1 ] + 1 ) ∗ p [ i ] f[i]=(f[i-1]+3*b[i-1]+3*a[i-1]+1)*p[i] f[i]=(f[i1]+3b[i1]+3a[i1]+1)p[i]
但是并不能A,因为只考虑了当前点,并不是前i个点,所以
f [ i ] = ( f [ i − 1 ] + 3 ∗ b [ i − 1 ] + 3 ∗ a [ i − 1 ] + 1 ) ∗ p [ i ] + f [ i − 1 ] ∗ ( 1 − p [ i ] ) f[i]=(f[i-1]+3*b[i-1]+3*a[i-1]+1)*p[i]+f[i-1]*(1-p[i]) f[i]=(f[i1]+3b[i1]+3a[i1]+1)p[i]+f[i1](1p[i])

心得

还要再看看……

??

为什么只有 f f f才考虑前 i i i和选到0的情况??

代码
#include
using namespace std;
const int N=1e5+4;
int n;
double a[N],b[N],f[N],p;
//i位成功的一次期望 i位成功的二次期望 前i答案(三次期望) 
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lf",&p);
		a[i]=(a[i-1]+1)*p;
		b[i]=(b[i-1]+2*a[i-1]+1)*p;
		f[i]=(f[i-1]+3*b[i-1]+3*a[i-1]+1)*p+f[i-1]*(1-p); 
	}
	printf("%.1lf",f[n]);
	return (0-0);
}

收集邮票

P4550

题意

和百事世界杯之旅有点像,不过第 i i i次需要支付 i i i元钱,问花费的期望

方法

高次期望
设需 x x x
a n s = x 2 + x 2 ans=\frac{x^2+x}{2} ans=2x2+x
所以需求出一个一次和两次
一次同上
a [ i ] a[i] a[i]还要i种没得到
a [ i ] = a [ i − 1 ] + n i a[i]=a[i-1]+\frac{n}{i} a[i]=a[i1]+in
二次
f [ i ] = n − i n ( f [ i ] + 2 ∗ a [ i ] + 1 ) + i n ( f [ i − 1 ] + 2 ∗ a [ i − 1 ] + 1 ) f[i]=\frac{n-i}{n}(f[i]+2*a[i]+1)+\frac{i}{n}(f[i-1]+2*a[i-1]+1) f[i]=nni(f[i]+2a[i]+1)+ni(f[i1]+2a[i1]+1)
f [ i ] = n − i i ( 2 ∗ a [ i ] + 1 ) + f [ i − 1 ] + 2 ∗ a [ i − 1 ] + 1 f[i]=\frac{n-i}{i}(2*a[i]+1)+f[i-1]+2*a[i-1]+1 f[i]=ini(2a[i]+1)+f[i1]+2a[i1]+1
a n s = a [ n ] + f [ n ] 2 ans=\frac{a[n]+f[n]}{2} ans=2a[n]+f[n]

代码
#include
using namespace std;
const int N=1e4+4;
double a[N],f[N];
int n;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		a[i]=a[i-1]+n*1.0/i;
		f[i]=(n-i)*1.0/i*(2*a[i]+1)+f[i-1]+2*a[i-1]+1;
	}
	printf("%.2lf",(a[n]+f[n])/2);
	return (0-0);
}

最小树形图

洛谷P4716

题意

求有向图的最小树形图

方法

求最短弧集合E;(每条边找一条连向自己的最小边)

判断集合E中有没有有向环,如果有转步骤3,否则转4;

收缩点,把有向环收缩成一个点,并且对图重新构建,包括边权值的改变和点的处理,之后再转步骤1;

展开收缩点,求得最小树形图;

心得

看看代码就懂了
还可以再看看

代码
#include
using namespace std;
const int N=104,M=1e4+4,inf=0x3f3f3f3f;
struct edge{
	int u,v,w;
}e[M];
int n,m,rt,ans=0,cnt,mn[N],from[N],vis[N],cn[N];
int main(){
	scanf("%d%d%d",&n,&m,&rt);
	for(int i=1;i<=m;i++)
		scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
	while(1){
		memset(mn,0x3f,sizeof(mn));
		for(int i=1,u,v,w;i<=m;i++){
			u=e[i].u;v=e[i].v;w=e[i].w;
			if(u!=v&&w<mn[v]){
				mn[v]=w;from[v]=u;
			}
		}
		for(int i=1;i<=n;i++)
			if(i!=rt&&mn[i]==inf){
				printf("-1");return (0-0);
			}
		cnt=0;
		memset(vis,0,sizeof(vis));
		memset(cn,0,sizeof(cn));
		for(int i=1,v;i<=n;i++){
			if(i==rt)continue;
			ans+=mn[i];
			v=i;
			while(vis[v]!=i&&!cn[v]&&v!=rt){
				vis[v]=i;
				v=from[v];
			}
			if(!cn[v]&&v!=rt){
				cn[v]=++cnt;
				for(int j=from[v];j!=v;j=from[j])
					cn[j]=cnt;
			}
		}
		if(!cnt)break;
		for(int i=1;i<=n;i++)
			if(!cn[i])cn[i]=++cnt;
		for(int i=1,u,v;i<=m;i++){
			u=e[i].u;v=e[i].v;
			e[i].u=cn[u];e[i].v=cn[v];
			if(cn[u]!=cn[v])e[i].w-=mn[v];//代替之前的边 
		}
		rt=cn[rt];
		n=cnt;
	}
	printf("%d",ans);
	return (0-0);
}

动态DP

洛谷P4719

题意

近期刷题小结9.24-10.11_第3张图片

方法

详见YY的PPT
线段树维护矩阵乘法

心得

还要再学习!!!
引用记得加‘&’

代码
#include
using namespace std;
inline int read(){
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f==1?x:-x;
}
const int N=1e5+4,inf=0x3f3f3f3f;
inline void MAX(int &x,int y){//!!!
    x=(x>y)?x:y;
}
struct matrix{
    int a[2][2];
    inline matrix operator *(const matrix &x)const{
        matrix ret;
        for(int i=0;i^2;i++)//i<2
            for(int j=0;j^2;j++){
                ret.a[i][j]=-inf;
                for(int k=0;k^2;k++)
                    MAX(ret.a[i][j],a[i][k]+x.a[k][j]); 
            }
        return ret;
    }
}t[N<<2],a[N];
struct edge{
    int v,nxt;
}e[N<<1];
int first[N],cnt=0;
inline void add(int u,int v){
    e[++cnt].v=v;e[cnt].nxt=first[u];first[u]=cnt;
}
int n,m,v[N];
int siz[N],fa[N],son[N],st[N],ed[N],top[N],idx[N],cn=0;
//ed[]每条重链的结束位置 
#define lc (p<<1)
#define rc ((p<<1)|1)
inline void pushup(int p){
    t[p]=t[lc]*t[rc];
}
inline void build(int p,int l,int r){
    if(l==r){
        t[p]=a[idx[l]];
        return;
    }
    int mid=l+r>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    pushup(p);
}
inline void modify(int p,int l,int r,int x){
    if(l==r){
        t[p]=a[idx[l]];
        return;
    }
    int mid=l+r>>1;
    if(x<=mid)modify(lc,l,mid,x);
    else modify(rc,mid+1,r,x);
    pushup(p);
}
inline matrix query(int p,int l,int r,int L,int R){
    if(L<=l&&r<=R)return t[p];
    int mid=l+r>>1;
    if(L<=mid&&R>mid)return query(lc,l,mid,L,R)*query(rc,mid+1,r,L,R);
    if(L<=mid)return query(lc,l,mid,L,R);
    if(R>mid)return query(rc,mid+1,r,L,R);
}
inline void change(int p,int x){//??
    a[p].a[1][0]+=x-v[p];//??
    //gx0会随vi改变而改变 
    v[p]=x;
    while(p){
        matrix pre=query(1,1,n,st[top[p]],ed[top[p]]);
        modify(1,1,n,st[p]);
        matrix cur=query(1,1,n,st[top[p]],ed[top[p]]);
        p=fa[top[p]];
        matrix &xx=a[p];
        xx.a[0][0]+=max(cur.a[0][0],cur.a[1][0])-max(pre.a[0][0],pre.a[1][0]);//??
        xx.a[0][1]=xx.a[0][0];
        xx.a[1][0]+=cur.a[0][0]-pre.a[0][0];
    }
}
inline void dfs(int x){
    siz[x]=1;
    for(int i=first[x],v;i;i=e[i].nxt){
        v=e[i].v;
        if(v==fa[x])continue;
        fa[v]=x;
        dfs(v);
        siz[x]+=siz[v];
        if(siz[son[x]]<siz[v])son[x]=v;
    }
}
inline pair<int,int> dfs(int x,int tp){//返回fx0,fx1 
    int gx0=0,gx1=0,fx0=0,fx1=0;
    pair<int,int>p;
    top[x]=tp;
    st[x]=++cn;
    idx[cn]=x;
    fx1=gx1=v[x];
    if(son[x]){
        p=dfs(son[x],tp);
        fx0+=max(p.first,p.second);
        fx1+=p.first;
    }
    else ed[tp]=cn;
    for(int i=first[x],v,y;i;i=e[i].nxt){
        v=e[i].v;
        if(v==fa[x]||v==son[x])continue;
        p=dfs(v,v);
        y=max(p.first,p.second);
        gx0+=y;fx0+=y;
        gx1+=p.first;fx1+=p.first;
    }
    a[x].a[0][0]=a[x].a[0][1]=gx0;
    a[x].a[1][0]=gx1;a[x].a[1][1]=-inf;
//    矩阵情况
//        | g_{i,0} g_{i,0} |
//  R_i = | g_{i,1}  -inf   |
    return make_pair(fx0,fx1);
}
int main(){
    n=read();m=read();
    for(int i=1;i<=n;i++)v[i]=read();
    for(int i=1,u,v;i<n;i++){
        u=read();v=read();
        add(u,v);add(v,u);
    }
    dfs(1);
    dfs(1,1);
    build(1,1,n);
    for(int i=1,x,y;i<=m;i++){
        x=read();y=read();
        change(x,y);
        matrix xx=query(1,1,n,st[1],ed[1]);
        printf("%d\n",max(xx.a[0][0],xx.a[1][0]));
        //根节点所在的重链底端的答案 
    }
    return (0-0);
} 

动态DP(加强版)

洛谷P4751

题意

同动态DP,强制在线,卡树链剖分

方法

全局平衡二叉树
重链建二叉树,轻边建虚边,只记父亲,不计儿子。
详见YY的PPT

心得

!!! v o i d void void函数若写成 i n t int int会出现鬼畜错误, R E RE RE!!!!!!!!!

代码
#include
using namespace std;
int read(){
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f==1?x:-x;
}
const int N=1e6+4,inf=0x3f3f3f3f;
struct edge{
    int v,nxt;
}e[N<<1];
int first[N],cnt=0;
void add(int u,int v){
    e[++cnt].v=v;e[cnt].nxt=first[u];first[u]=cnt;
}
void MAX(int &x,int y){//!!!!!
	x=(x>y?x:y);
}
struct matrix{
	int a[2][2];
	matrix(){
		a[0][0]=a[1][0]=a[0][1]=a[1][1]=-inf;
	}
	int operator =(const int x){
		return a[0][0]=a[1][1]=0;//
	}
	int* operator [](const int &x){
		return a[x];
	}
	matrix operator *(const matrix &b)const{
		matrix ret;
		ret.a[0][0]=max(a[0][0]+b.a[0][0],a[0][1]+b.a[1][0]);
		ret.a[0][1]=max(a[0][0]+b.a[0][1],a[0][1]+b.a[1][1]);
		ret.a[1][0]=max(a[1][0]+b.a[0][0],a[1][1]+b.a[1][0]);
		ret.a[1][1]=max(a[1][0]+b.a[0][1],a[1][1]+b.a[1][1]);//比循环快 
		return ret;//!!! 
	}
}w[N],mul[N];
int n,m,v[N],ans=0;
int siz[N],son[N];
void dfs(int x){
	siz[x]=1;
	for(int i=first[x],v;i;i=e[i].nxt){
		v=e[i].v;
		if(siz[v])continue;
		dfs(v);
		siz[x]+=siz[v];
		if(siz[son[x]]<siz[v])son[x]=v;
	}
}
int rt,vis[N],fa[N],s[N][2],sum[N],st[N];
void pushup(int p){
	mul[p]=mul[s[p][0]]*w[p]*mul[s[p][1]];//
}
bool isson(int p){
	return s[fa[p]][0]!=p&&s[fa[p]][1]!=p;
}
int build(int l,int r){
	if(l>r)return 0;
	int mid=lower_bound(sum+l,sum+r+1,(sum[r]-sum[l-1]-1)/2+1+sum[l-1])-sum;
	int x=st[mid];
	s[x][0]=build(l,mid-1);
	s[x][1]=build(mid+1,r);
	fa[s[x][0]]=fa[s[x][1]]=x;
	pushup(x);
	return x;
}
int tbuild(int x){
	for(int i=x;i;i=son[i])
		vis[i]=1;
	for(int k=x,v,nwrt;k;k=son[k])
		for(int i=first[k];i;i=e[i].nxt){
			v=e[i].v;
			if(vis[v])continue;
			nwrt=tbuild(v);
			fa[nwrt]=k;
			w[k][0][0]+=max(mul[nwrt][0][0],mul[nwrt][1][0]);
			w[k][0][1]=w[k][0][0];
			w[k][1][0]+=mul[nwrt][0][0]; 
		}
	int len=0;
	sum[0]=0;
	for(int i=x;i;i=son[i]){
		st[++len]=i;
		sum[len]=sum[len-1]+siz[i]-siz[son[i]];//前缀和 
	}
	return build(1,len); 
}
void init(){
	w[0]=mul[0]=0;
	for(int i=1;i<=n;i++){
		v[i]=w[i][1][0]=read();
		w[i][0][0]=w[i][0][1]=0;
	}
}
void modify(int p,int x){
	w[p][1][0]+=x-v[p];
	v[p]=x;
	while(p){
		int f=fa[p];
		if(f&&isson(p)){//虚儿子
			matrix pre=mul[p];
			pushup(p);
			matrix cur=mul[p];
			w[f][0][0]+=max(cur[0][0],cur[1][0])-max(pre[0][0],pre[1][0]);
			w[f][0][1]=w[f][0][0];
			w[f][1][0]+=cur[0][0]-pre[0][0]; 
		}
		else pushup(p);
		p=f;
	}
}
int main(){
    n=read();m=read(); 
    init();
    for(int i=1,u,v;i<n;i++){
        u=read();v=read();
        add(u,v);add(v,u);
    }
    dfs(1);
    rt=tbuild(1);
    for(int i=1,x,y;i<=m;i++){
    	x=read()^ans;
		y=read();
    	modify(x,y);
    	printf("%d\n",ans=max(mul[rt][0][0],mul[rt][1][0]));
	} 
    return (0-0);
} 

残缺的字符串

P4173

题意

AB两个字符串有些字符缺失,用*表示,可以是任何字符,问A在B中能匹配几次。

方法

NTT
将通配字符设置为0;
g ( x ) = ∑ i = 1 m a [ x ] ∗ b [ x − i + 1 ] ∗ ( a [ x ] − b [ x − i + 1 ] ) 2 g(x)=\sum^{m}_{i=1}{a[x]*b[x-i+1]*(a[x]-b[x-i+1])^2} g(x)=i=1ma[x]b[xi+1](a[x]b[xi+1])2
g ( x ) = ∑ i = 1 m a [ x ] ∗ b [ x − i + 1 ] 3 − ∑ i = 1 m 2 ∗ a [ x ] 2 ∗ b [ x − i + 1 ] 2 + ∑ i = 1 m a [ x ] 3 ∗ b [ x − i + i ] g(x)=\sum^{m}_{i=1}a[x]*b[x-i+1]^3-\sum^{m}_{i=1}2*a[x]^2*b[x-i+1]^2+\sum^{m}_{i=1}a[x]^3*b[x-i+i] g(x)=i=1ma[x]b[xi+1]3i=1m2a[x]2b[xi+1]2+i=1ma[x]3b[xi+i]
x x x换成 m − x m-x mx这样,加起来为定值方便卷积
g ( m + 1 ) = ∑ i = 1 m a [ m − x ] ∗ b [ x − i + 1 ] 3 − ∑ i = 1 m 2 ∗ a [ m − x ] 2 ∗ b [ x − i + 1 ] 2 + ∑ i = 1 m a [ m − x ] 3 ∗ b [ x − i + i ] g(m+1)=\sum^{m}_{i=1}a[m-x]*b[x-i+1]^3-\sum^{m}_{i=1}2*a[m-x]^2*b[x-i+1]^2+\sum^{m}_{i=1}a[m-x]^3*b[x-i+i] g(m+1)=i=1ma[mx]b[xi+1]3i=1m2a[mx]2b[xi+1]2+i=1ma[mx]3b[xi+i]
分开把系数转点值,按式子算了之后再转系数
注意最后卷起来为两边下标相加, m − 1 m-1 m1

心得

x x x换成 m − x m-x mx这样,加起来为定值方便卷积
注意最后卷起来为两边下标相加, m − 1 m-1 m1

代码
#include
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
#define mod 998244353
#define ll long long
const int N=1e6+4;
inline int ksm(int x,int r){
	int ret=1;
	for(int i=0;(1<<i)<=r;i++){
		if((1<<i)&r)ret=(ll)ret*x%mod;
		x=(ll)x*x%mod;
	}
	return ret;
}
int rev[N<<1];
inline void NTT(int *a,int lim,int len,int fl){
	for(int i=0;i<len;i++){
		rev[i]=(rev[i>>1]>>1)|((i&1)<<lim-1);
		if(i<rev[i])swap(a[i],a[rev[i]]);
	}
	for(int mid=1,tmp,x,u,v;mid<len;mid<<=1){
		tmp=ksm(3,(mod-1)/(mid<<1));
		if(fl==-1)tmp=ksm(tmp,mod-2);
		for(int i=0;i<len;i+=(mid<<1)){
			x=1;
			for(int j=0;j<mid;j++,x=(ll)x*tmp%mod){
				u=a[i+j];v=(ll)x*a[i+j+mid]%mod;
				a[i+j]=(u+v)%mod;a[i+j+mid]=(u-v+mod)%mod;
			}
		}
	}
}
int n,m,lim=0,len=1;
char aa[N],bb[N];
int a[N<<1],b[N<<1],A[N<<1],B[N<<1],C[N<<1],ans[N];
int main(){
	m=read();n=read();
	scanf("%s%s",aa,bb);
	for(int i=0,j=m-1;i<m;i++,j--)
		if(aa[j]!='*')a[i]=aa[j]-'a'+1;
	for(int i=0;i<n;i++)
		if(bb[i]!='*')b[i]=bb[i]-'a'+1;
	while(len<m+n){
		len<<=1;
		lim++;
	}
	
	for(int i=0;i<len;i++){
		A[i]=(ll)a[i]*a[i]*a[i]%mod;
		B[i]=b[i];
	}
	NTT(A,lim,len,1);NTT(B,lim,len,1);
	for(int i=0;i<len;i++)
		C[i]=(ll)A[i]*B[i]%mod;
	
	for(int i=0;i<len;i++){
		A[i]=(ll)a[i]*a[i]%mod;
		B[i]=(ll)b[i]*b[i]%mod;
	}
	NTT(A,lim,len,1);NTT(B,lim,len,1);
	for(int i=0;i<len;i++)
		C[i]=((ll)C[i]-(ll)2*A[i]*B[i]%mod+mod)%mod;
		
	
	for(int i=0;i<len;i++){
		A[i]=a[i];
		B[i]=(ll)b[i]*b[i]*b[i]%mod;
	}
	NTT(A,lim,len,1);NTT(B,lim,len,1);
	for(int i=0;i<len;i++)
		C[i]=((ll)C[i]+(ll)A[i]*B[i]%mod)%mod;
	
	NTT(C,lim,len,-1);
	int tmp=ksm(len,mod-2);
	for(int i=0;i+m-1<n;i++)
		if(!(C[i+m-1]*tmp))ans[++ans[0]]=i+1;//
	printf("%d\n",ans[0]);
	for(int i=1;i<=ans[0];i++)
		printf("%d ",ans[i]);
	return (0-0);
}

严格次小生成树[BJWC2010]

洛谷P4180

题意

求无向图的严格次小生成树

方法

倍增
每次枚举加上一条边,删除环上最大边,由于严格次小,维护两个倍增数组,最大和次大,
复杂度 O ( n l o g m ) O(nlog_m) O(nlogm)

心得

修改代码,一定要把所有相关的地方修改完!!!

代码
#include
using namespace std;
#define int long long
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} 
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=1e5+4,M=3e5+4;
struct node{
	int u,v,w,fl;
}a[M];
struct edge{
	int v,w,nxt;
}e[N<<1];
int first[N],cnt=0;
inline void add(int u,int v,int w){
	e[++cnt].v=v;e[cnt].w=w;
	e[cnt].nxt=first[u];first[u]=cnt;
}
inline bool comp(const node &x,const node &y){
	return x.w<y.w;
}
int n,m,fa[N][20],mx[N][20],fat[N],preans=0,ans=1e18,mx2[N][20];
//严格次小还要记次大值 
inline int find(int x){
	return x==fat[x]?x:fat[x]=find(fat[x]);
}
int dep[N];
inline void update(int &mx,int &mx2,int x,int y){
	if(x>mx){mx2=mx;mx=x;}
	else if(x>mx2&&mx!=x)mx2=x;
	if(y>mx){mx2=mx;mx=y;}
	else if(y>mx2&&mx!=y)mx2=y;
}
inline void predfs(int x){
	for(int i=1;(1<<i)<=dep[x];i++){
		fa[x][i]=fa[fa[x][i-1]][i-1];
		update(mx[x][i],mx2[x][i],mx[x][i-1],mx2[x][i-1]);
		update(mx[x][i],mx2[x][i],mx[fa[x][i-1]][i-1],mx2[fa[x][i-1]][i-1]);
	}
	for(int i=first[x],v;i;i=e[i].nxt){
		v=e[i].v;
		if(v==fa[x][0])continue;
		fa[v][0]=x;
		dep[v]=dep[x]+1;
		mx[v][0]=e[i].w;
		predfs(v);
	}
}
inline int query(int x,int y,int d){
	int ret=0,ret2=0,tmp;
	if(dep[x]<dep[y])swap(x,y);
	tmp=dep[x]-dep[y];
	for(int i=0;(1<<i)<=tmp;i++)
		if((1<<i)&tmp){
			update(ret,ret2,mx[x][i],mx2[x][i]);
			x=fa[x][i];
		}
	if(x==y)return ret==d?ret2:ret;//!!
	for(int i=19;i>=0;i--)//
		if(fa[x][i]!=fa[y][i]){
			update(ret,ret2,mx[x][i],mx2[x][i]); 
            update(ret,ret2,mx[y][i],mx2[y][i]); 
			x=fa[x][i];y=fa[y][i];
		}
	update(ret,ret2,mx[x][0],mx2[x][0]); 
    update(ret,ret2,mx[y][0],mx2[y][0]); 
	return ret==d?ret2:ret;
}
signed main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)fat[i]=i;
	for(int i=1;i<=m;i++)
		a[i]=(node){read(),read(),read(),0};
	sort(a+1,a+m+1,comp);
	for(int i=1,fu,fv;i<=m;i++){
		fu=find(a[i].u);fv=find(a[i].v);
		if(fu==fv)continue;
		fat[fu]=fv;
		a[i].fl=1;
		preans+=a[i].w;
		add(a[i].u,a[i].v,a[i].w);
		add(a[i].v,a[i].u,a[i].w);
	}
	dep[1]=1;
	predfs(1);
	for(int i=1,t;i<=m;i++){
		if(a[i].fl)continue;
		t=preans+a[i].w-query(a[i].u,a[i].v,a[i].w);
		if(t>preans&&t<ans)ans=t;
	}
	cout<<ans;
	return (0-0);
}

[HNOI2008]玩具装箱TOY

洛谷3195

题意

近期刷题小结9.24-10.11_第4张图片

方法

斜率优化
C k + + C_k++ Ck++
L + + L++ L++
费用为
( ∑ k = i j C k − L ) 2 (\sum^{j}_{k=i}{C_k}-L)^2 (k=ijCkL)2
f [ i ] f[i] f[i]表示放前i的玩具需要的费用
f [ i ] = m i n ( f [ k ] + ( s [ i ] − s [ k − 1 ] − L ) 2 ) f[i]=min(f[k]+(s[i]-s[k-1]-L)^2) f[i]=min(f[k]+(s[i]s[k1]L)2)
a [ i ] = s [ i ] − L ; b [ i ] = s [ i − 1 ] ; a[i]=s[i]-L;b[i]=s[i-1]; a[i]=s[i]L;b[i]=s[i1];
f [ i ] = m i n ( f [ k ] + a [ i ] 2 + b [ k ] 2 + 2 ∗ a [ i ] ∗ b [ k ] ) f[i]=min(f[k]+a[i]^2+b[k]^2+2*a[i]*b[k]) f[i]=min(f[k]+a[i]2+b[k]2+2a[i]b[k])
f [ k ] + b [ k ] 2 = 2 ∗ a [ i ] ∗ b [ k ] − a [ i ] 2 + f [ i ] f[k]+b[k]^2=2*a[i]*b[k]-a[i]^2+f[i] f[k]+b[k]2=2a[i]b[k]a[i]2+f[i]
所以是一条斜率为 2 ∗ a [ i ] 2*a[i] 2a[i],截距为 a [ i ] 2 + f [ i ] a[i]^2+f[i] a[i]2+f[i]的直线
2 ∗ a [ i ] 2*a[i] 2a[i]单调递增,所以最优点组成的凸包斜率也因单调递增。

心得

还不是很熟!!!!!!!!!!!
仔细思考
换元法降低式子复杂程度!!!

代码
#include
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} 
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
#define ll long long
const int N=5e4+4;
int n,L;
ll a[N],f[N];
inline ll s1(int x){
	return a[x]+x;
}
inline ll s2(int x){
	return s1(x)+L+1;
}
inline ll X(int x){
	return s2(x);
}
inline ll Y(int x){
	return f[x]+s2(x)*s2(x);
}
inline double check(int x,int y){
	return ((double)Y(x)-Y(y))/(X(x)-X(y));
}
int head=1,tail=1,q[N];
int main(){
	n=read();L=read();
	for(int i=1;i<=n;i++)
		a[i]=read()+a[i-1];
	for(int i=1;i<=n;i++){
		while(head<tail&&check(q[head],q[head+1])<2*s1(i))head++;
		f[i]=f[q[head]]+(s1(i)-s2(q[head]))*(s1(i)-s2(q[head]));
		while(head<tail&&check(i,q[tail-1])<check(q[tail-1],q[tail]))tail--;
		q[++tail]=i;
	}
	cout<<f[n];
	return (0-0);
}

植树节

题意

求删去一条边后,无向图的最小生成树

方法

先求出最小生成树
若删去不在树上的边就不管
考虑加上某一条边后,哪些边就可以删除了,一定是两个端点在树上路径上的边,于是用树链剖分维护路径最小值。

心得

之前做起的题,现在都不会了~~~

代码
#include
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=3e5+4;
struct edge{
	int u,v,w,nxt,id;
}a[N],e[N<<1];
int first[N],cnt=0;
int n,m,q,fa[N],use[N],dy[N];
inline bool comp(const edge &x,const edge &y){
	return x.w<y.w;
}
inline int find(int x){
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
inline int add(int u,int v,int w){
	e[++cnt].u=u;e[cnt].v=v;e[cnt].w=w;
	e[cnt].nxt=first[u];first[u]=cnt;
}
int siz[N],dep[N],son[N],pos[N],idx[N],top[N],pos_ed[N],tot=1;
inline void dfs1(int u){
	siz[u]=1;
	for(int i=first[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(v==fa[u])continue;
		fa[v]=u;
		dep[v]=dep[u]+1;
		dfs1(v);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}
inline void dfs2(int u){
	if(son[u]){
		pos[son[u]]=++tot;
		idx[pos[son[u]]]=son[u]; 
		top[son[u]]=top[u];
		dfs2(son[u]);
	}
	for(int i=first[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(v==fa[u]||v==son[u])continue;
		pos[v]=++tot;
		idx[pos[v]]=v;
		top[v]=v;
		dfs2(v);
	}
	pos_ed[u]=tot;
}
#define ll long long
#define lc (p<<1)
#define rc (p<<1|1)
struct node{
	int l,r,lazy,sum;
}t[(N<<2)];
inline void build(int p,int l,int r){
	t[p].l=l;t[p].r=r;t[p].lazy=2e9;t[p].sum=2e9;
	if(l==r)return;
	int mid=l+r>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
}
inline void init(){
	dfs1(1);
	top[1]=idx[1]=1;
	pos[1]=tot=1;
	dfs2(1);
	build(1,1,n);
}
inline void pushdown(int p){
	if(t[p].lazy==2e9)return;
	t[lc].sum=min(t[lc].sum,t[p].lazy);
	t[lc].lazy=min(t[lc].lazy,t[p].lazy);
	t[rc].sum=min(t[rc].sum,t[p].lazy);
	t[rc].lazy=min(t[rc].lazy,t[p].lazy);
	t[p].lazy=2e9; 
}
inline void add1(int p,int l,int r,int v){
	if(l<=t[p].l&&t[p].r<=r){
		t[p].sum=min(t[p].sum,v);
		t[p].lazy=min(t[p].lazy,v);
		return;
	}
	pushdown(p);
	int mid=t[p].l+t[p].r>>1;
	if(l<=mid)add1(lc,l,r,v);
	if(mid<r)add1(rc,l,r,v);
	t[p].sum=min(t[lc].sum,t[rc].sum);
}
inline int query(int p,int l,int r){
	if(l<=t[p].l&&t[p].r<=r)
		return t[p].sum;
	pushdown(p);
	int mid=t[p].l+t[p].r>>1,ans=2e9;
	if(l<=mid)ans=min(ans,query(lc,l,r));
	if(mid<r)ans=min(ans,query(rc,l,r));
	return ans;
}
inline void pathadd(int u,int v,int w){
	if(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		pathadd(fa[top[u]],v,w);
		add1(1,pos[top[u]],pos[u],w);
		return;
	}
	if(dep[u]>dep[v])swap(u,v);
	add1(1,pos[u]+1,pos[v],w);//
}
int main(){
	int size = 8 << 20;
	char *p = (char*)malloc(size) + size;
	__asm__("movl %0, %%esp\n" :: "r"(p));
	n=read();m=read();
	for(int i=1;i<=m;i++){
		a[i].u=read();a[i].v=read();a[i].w=read();a[i].id=i;
	}
	sort(a+1,a+m+1,comp);
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=m;i++)dy[a[i].id]=i;
	int ans=0,rr=1;
	for(int i=1,u,v,fu,fv;i<=m;i++){
		fu=find(a[i].u);fv=find(a[i].v);
		if(fu!=fv){
			add(a[i].u,a[i].v,a[i].w);add(a[i].v,a[i].u,a[i].w);
			fa[fu]=fv;
			rr++;
			ans+=a[i].w;
			use[i]=1;
		}
	}
	memset(fa,0,sizeof(fa));
	init();
	for(int i=1;i<=m;i++){
		if(use[i])continue;
		pathadd(a[i].u,a[i].v,a[i].w);
	}
	q=read();
	for(int i=1,x,u,v,r;i<=q;i++){
		x=dy[read()];
		if(rr<n){printf("Not connected\n");continue;}
		if(!use[x]){printf("%d\n",ans);continue;}
		u=a[x].u;v=a[x].v;
		if(dep[u]<dep[v])swap(u,v);
		r=query(1,pos[u],pos[u]);
		if(r==2e9)printf("Not connected\n");
		else printf("%d\n",ans-a[x].w+r);
	}
	return (0-0);
}

你可能感兴趣的:(学习小结)