近期刷題小結9.24-10.11

NOIP2016提高組換教室

洛谷P1850

題意

兩個序列cc,dd,你需依次走過這nn個點,如果申請,則有ppi 的機會將cci 換成ddi,但你只能申請mm次,求走過這nn個點的路徑總和的期望最小值。

方法

期望DPDP

f[i][j][0/1]f[i][j][0/1]表示走過前ii個點,當前點是否申請的期望

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][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][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]=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][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]]);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]]);
會精度丟失,WA3個點

心得

終於會期望了,不過對於doubledouble精度丟失很懵逼

ff數組一定要清零,以免用到一些不存在的狀態!!!

代碼
#include<bits/stdc++.h>
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

題意

區間翻轉,輸出最終區間

方法

splaysplay
下標建樹,對於翻轉的區間,通過splaysplay形成一棵子樹,打翻轉標記,pushdownpushdown時交換左右兒子,只有在findfind時才pushdownpushdown?

代碼
#include<bits/stdc++.h>
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

題意

支持操作
在這裏插入圖片描述

方法

splaysplay
每次插入把根節點的右兒子的左兒子弄空,直接在上面建樹
每次刪除操作,把要刪除的點轉到一棵子樹上,直接刪除

心得

沒幾次默寫splaysplay,寫掛了,調不出來
寫代碼時一定要認真,寫錯一個半天都調不出來!!!
變量一定要附初值!!!

代碼
#include<bits/stdc++.h>
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<bits/stdc++.h>
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

題意

支持操作
在這裏插入圖片描述
讀入的時候直接getchargetchar即可
注:GETGET操作如果得到的是’\n’,那麼輸出’\n’後無需再輸出一個換行符

方法

splaysplay
此題比上一道多了區間翻轉,像文藝平衡書一樣翻轉即可

心得

Yeah!Yeah!

代碼
#include<bits/stdc++.h>
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

題意

nn個不同的數,每個數出現的概率相同,平均需要幾次才能湊齊所有的數
用分數輸出答案

方法

數學期望
f[k]f[k]表示還剩kk個數沒有的次數期望
f[k]=nknf[k]+knf[k1]+1f[k]=\frac{n-k}{n}f[k]+\frac{k}{n}f[k-1]+1
f[k]=f[k1]+nkf[k]=f[k-1]+\frac{n}{k}

心得

期望入門題,tcltcl
longlonglonglonglldlld輸出

代碼
#include<bits/stdc++.h>
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

題意

nn個數的序列,每次有p[i]p[i]的概率爲1,否則爲0,連續xx個1的貢獻爲xx3,求期望分數。

方法

高次期望
先考慮一次的情況
a[i]=(a[i1]+1)p[i]a[i]=(a[i-1]+1)*p[i]
二次
b[i]=(a[i1]+1)2p[i]b[i]=(a[i-1]+1)^2*p[i]
b[i]=(b[i1]+2a[i1]+1)p[i]b[i]=(b[i-1]+2*a[i-1]+1)*p[i]
三次
f[i]=(a[i1]+1)3p[i]f[i]=(a[i-1]+1)^3*p[i]
f[i]=(f[i1]+3b[i1]+3a[i1]+1)p[i]f[i]=(f[i-1]+3*b[i-1]+3*a[i-1]+1)*p[i]
但是並不能A,因爲只考慮了當前點,並不是前i個點,所以
f[i]=(f[i1]+3b[i1]+3a[i1]+1)p[i]+f[i1](1p[i])f[i]=(f[i-1]+3*b[i-1]+3*a[i-1]+1)*p[i]+f[i-1]*(1-p[i])

心得

還要再看看……

??

爲什麼只有ff才考慮前ii和選到0的情況??

代碼
#include<bits/stdc++.h>
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

題意

和百事世界盃之旅有點像,不過第ii次需要支付ii元錢,問花費的期望

方法

高次期望
設需xx
ans=x2+x2ans=\frac{x^2+x}{2}
所以需求出一個一次和兩次
一次同上
a[i]a[i]還要i種沒得到
a[i]=a[i1]+nia[i]=a[i-1]+\frac{n}{i}
二次
f[i]=nin(f[i]+2a[i]+1)+in(f[i1]+2a[i1]+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]=nii(2a[i]+1)+f[i1]+2a[i1]+1f[i]=\frac{n-i}{i}(2*a[i]+1)+f[i-1]+2*a[i-1]+1
ans=a[n]+f[n]2ans=\frac{a[n]+f[n]}{2}

代碼
#include<bits/stdc++.h>
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<bits/stdc++.h>
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

題意

在這裏插入圖片描述

方法

詳見YY的PPT
線段樹維護矩陣乘法

心得

還要再學習!!!
引用記得加‘&’

代碼
#include<bits/stdc++.h>
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

心得

!!!voidvoid函數若寫成intint會出現鬼畜錯誤,RERE!!!!!!!!!

代碼
#include<bits/stdc++.h>
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=1ma[x]b[xi+1](a[x]b[xi+1])2g(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]3i=1m2a[x]2b[xi+1]2+i=1ma[x]3b[xi+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]
xx換成mxm-x這樣,加起來爲定值方便卷積
g(m+1)=i=1ma[mx]b[xi+1]3i=1m2a[mx]2b[xi+1]2+i=1ma[mx]3b[xi+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]
分開把係數轉點值,按式子算了之後再轉系數
注意最後捲起來爲兩邊下標相加,m1m-1

心得

xx換成mxm-x這樣,加起來爲定值方便卷積
注意最後捲起來爲兩邊下標相加,m1m-1

代碼
#include<bits/stdc++.h>
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(nlogm)O(nlog_m)

心得

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

代碼
#include<bits/stdc++.h>
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

題意

在這裏插入圖片描述

方法

斜率優化
Ck++C_k++
L++L++
費用爲
(k=ijCkL)2(\sum^{j}_{k=i}{C_k}-L)^2
f[i]f[i]表示放前i的玩具需要的費用
f[i]=min(f[k]+(s[i]s[k1]L)2)f[i]=min(f[k]+(s[i]-s[k-1]-L)^2)
a[i]=s[i]L;b[i]=s[i1];a[i]=s[i]-L;b[i]=s[i-1];
f[i]=min(f[k]+a[i]2+b[k]2+2a[i]b[k])f[i]=min(f[k]+a[i]^2+b[k]^2+2*a[i]*b[k])
f[k]+b[k]2=2a[i]b[k]a[i]2+f[i]f[k]+b[k]^2=2*a[i]*b[k]-a[i]^2+f[i]
所以是一條斜率爲2a[i]2*a[i],截距爲a[i]2+f[i]a[i]^2+f[i]的直線
2a[i]2*a[i]單調遞增,所以最優點組成的凸包斜率也因單調遞增。

心得

還不是很熟!!!!!!!!!!!
仔細思考
換元法降低式子複雜程度!!!

代碼
#include<bits/stdc++.h>
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<bits/stdc++.h>
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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章