20200512考试总结

(1)遇到最优化(F(i)*G(j)-G(i)*F(j))形式的式子要敏锐地联想到向量叉积,可以考虑数形结合,化为求凸包面积

(2)求凸包时要限制先求出来的前半部分凸包不被弹出

(3)选择一个区间加上某个数---->差分之后相当于选两个数一个+v一个-v,虽说是套路,但这的确是许多题目的突破口

(4)根号分治/平衡,不止是对序列的分治,也可以是进行sqrt(n)次操作之后重构一下(有KD树的思想)

虽然看起来没有什么用(因为大多数时候都是接近2亿的复杂度了),但是实践起来却有奇效,可以水过一些题

 

20200513考试总结

(1)T1自己想出了一个分治的做法,结果只有70分,后来发现是拼盘的暴力RE了,删掉就A了

分治的做法好像是最快的,虽然正解是FMT(FWT的或卷积)

简单讲讲吧,我发现ans与每一层的异或和sum是有规律的

大概就是一个斜向右下放的异或杨辉三角

我们发现,如果知道所有1的位置并且高效地把每一个数的贡献异或进答案

这样做的时间复杂度为O(n^2*(\frac{3}{4})^{log_2(n)}),根本无法通过

我们发现有一些部分是重复计算了的

 sum[1~8]对ans[1~8]的贡献与它对ans[9~16]的贡献是一样的

于是我们再完成对ans[1~8]的贡献之后,可以直接把ans[9~16]分别异或上ans[1~8]的值

再来处理sum[9~10]对ans[1~8]的贡献

我们发现这样是可以分治下去解决的

时间复杂度为T(n)=2*T(n/2)+O(n)=O(nlogn)

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 300005
int fir[N],nxt[2*N],to[2*N],cnt;
void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
int ans[N],val[N];
int sum[N],mxd;
void dfs_60(int u,int ff,int d)
{
	sum[d]^=val[u];mxd=max(mxd,d);
	for(int v,p=fir[u];p;p=nxt[p])
		if((v=to[p])!=ff)
			dfs_60(v,u,d+1);
}
int tmp[20][N];
void solve(int l,int r,int yl,int yr,int d)
{
	if(l==r&&yl==yr){
		tmp[d][l]^=sum[yl];
		return;
	}
	int mid=(l+r)>>1,ymid=(yl+yr)>>1;
	solve(l,mid,yl,ymid,d);
	for(int i=l;i<=mid;i++)
		tmp[d][i-l+1+mid]^=tmp[d][i];
	solve(l,mid,ymid+1,yr,d+1);
	for(int i=l;i<=mid;i++)
		tmp[d][i]^=tmp[d+1][i],tmp[d+1][i]=0;
}
int main()
{
	int n,Q,i,u,v;
	n=gi();Q=gi();
	for(i=1;i<n;i++){u=gi();v=gi();adde(u,v);}
	for(i=1;i<=n;i++)val[i]=gi();
	ans[0]=val[1];
	dfs_60(1,0,1);
	int len=1;
	while(len<mxd)len<<=1;
	solve(1,len,1,len,1);
	tmp[1][0]=val[1];
	for(i=1;i<=Q;i++){
		u=gi()%len;
		printf("%d\n",tmp[1][u]);
	}
}

 

(2)拆分计算贡献,不要思维僵化

(3)分治NTT尽量写vector,否则会有一大堆细节,而且常数会巨大(坑了我一下午)

(4)生成函数:把一些复杂的多项式写成生成函数的闭形式再化简会简化运算与码量

(5)树链剖分时对每一个链顶单独开一个线段树可以优化常数并且简化query操作

竹取飞翔

image

image

又是毒瘤数据结构,有两种思路:利用相交路径的LCA必在某一条路径上的性质,把权值分为va,vb 两种来用线段树维护

第二种时利用相交路径的点数-边数为1的性质,一次修改就是点加边减,再来求答案

这里只讲第一种

首先要学会线段树维护端点加权的最大子段和(2KB)

然后需要对每一个点开一个multiset,维护轻儿子所在链上的最大va前缀和

然后就是各种细节,注意端点加权的值不能实际地加入线段树中,否则会直接把子段上每一个加权都计算进去,即使是差分之后也不行,因为前一个点的差分会影响到最优的子段

只能单独写一个insertc函数

代码:(4.4KB)(此生写过的最长代码)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 100005
#define LL long long
#define lc a[i].LC
#define rc a[i].RC
multiset<LL> S[N],ANS;
multiset<LL>::iterator it1,it2,it;
struct node{
	int LC,RC,l,r;
	LL va,vab,lb;
	LL lx,rx,mx;
	LL tans,ans;
}a[N<<2];
int T[N],tot;
void pushup(int i)
{
	a[i].va=a[lc].va+a[rc].va;
	a[i].vab=a[lc].vab+a[rc].vab;
	a[i].lx=max(a[lc].lx,a[lc].va+a[rc].lx);
	a[i].rx=max(a[rc].rx,a[lc].rx+a[rc].va);
	a[i].mx=max(a[lc].rx+a[rc].lx,max(a[lc].mx,a[rc].mx));
	a[i].tans=max(a[lc].tans,a[rc].tans);
	a[i].ans=max(a[i].tans,a[i].mx);
}
void cal(int i,LL k)
{
	a[i].tans+=k;a[i].ans+=k;
	a[i].vab+=k;
	a[i].rx+=k;a[i].mx+=k;
	a[i].lb+=k;
}
void pushdown(int i)
{
	if(a[i].lb&&a[i].l<a[i].r){
		cal(lc,a[i].lb);cal(rc,a[i].lb);
		a[i].lb=0;
	}
}
void build(int &i,int l,int r)
{
	i=++tot;a[i].l=l;a[i].r=r;
	if(l==r)return;
	int mid=(l+r)>>1;
	build(lc,l,mid);build(rc,mid+1,r);
}
void inserta(int i,int x,LL k)
{
	if(a[i].l>x||a[i].r<x)return;
	pushdown(i);
	if(a[i].l==x&&a[i].r==x){
		a[i].va+=k;a[i].vab+=k;
		a[i].lx+=k;a[i].rx+=k;
		a[i].tans+=k;a[i].ans+=k;
		return;
	}
	inserta(lc,x,k);inserta(rc,x,k);
	pushup(i);
}
void insertb(int i,int l,int r,int k)
{
	if(a[i].l>r||a[i].r<l)return;
	pushdown(i);
	if(l<=a[i].l&&a[i].r<=r){cal(i,k);return;}
	insertb(lc,l,r,k);insertb(rc,l,r,k);
	pushup(i);
}
void insertc(int i,int x,LL k)
{
	if(a[i].l>x||a[i].r<x)return;
	pushdown(i);
	if(a[i].l==x&&a[i].r==x){
		a[i].lx+=k;a[i].rx+=k;
		a[i].ans=max(a[i].mx,a[i].tans);
		return;
	}
	insertc(lc,x,k);insertc(rc,x,k);
	pushup(i);
}
LL Vc(int t)
{
	if(S[t].empty())return 0ll;
	LL ret=0;it=S[t].end();
	it--;ret+=*it;
	if(S[t].size()==1)return ret;
	it--;ret+=*it;
	return ret;
}
void fresh(int i,int x,int pos)
{
	if(x<a[i].l||a[i].r<x)return;
	pushdown(i);
	if(a[i].l==x&&a[i].r==x){
		a[i].tans=a[i].vab+Vc(pos);
		a[i].ans=max(a[i].mx,a[i].tans);
		return;
	}
	fresh(lc,x,pos);fresh(rc,x,pos);
	pushup(i);
}
int fir[N],to[2*N],nxt[2*N],cnt;
void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
int fa[N],siz[N],son[N],dep[N],top[N],lim[N],dfn[N];
void dfs1(int u)
{
	dep[u]=dep[fa[u]]+1;siz[u]=1;
	for(int v,p=fir[u];p;p=nxt[p]){
		if((v=to[p])!=fa[u]){
			fa[v]=u;dfs1(v);siz[u]+=siz[v];
			if(siz[son[u]]<siz[v])son[u]=v;
		}
	}
}
void dfs2(int u)
{
	dfn[u]=dep[u]-dep[top[u]]+1;
	lim[top[u]]=max(lim[top[u]],dfn[u]);
	if(son[u]){top[son[u]]=top[u];dfs2(son[u]);}
	else ANS.insert(0);
	for(int v,p=fir[u];p;p=nxt[p]){
		if((v=to[p])!=fa[u]&&v!=son[u]){
			top[v]=v;dfs2(v);
			S[u].insert(0);
			build(T[v],1,lim[v]);
		}
	}
	ANS.insert(0);
}
void modify(int x,int y,LL k)
{
	int p,t;LL pre;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		ANS.erase(ANS.find(a[T[top[x]]].ans));
		insertb(T[top[x]],1,dfn[x],k);
		ANS.insert(a[T[top[x]]].ans);;
		x=fa[top[x]];
	}
	if(dep[x]<dep[y])swap(x,y);
	if(x!=y){
		ANS.erase(ANS.find(a[T[top[x]]].ans));
		insertb(T[top[x]],dfn[y]+1,dfn[x],k);
		ANS.insert(a[T[top[x]]].ans);
	}
	bool flg=0;
	for(;y;y=p){
		p=fa[top[y]];t=top[y];pre=0;
		if(p){
			pre=*S[p].rbegin();
			S[p].erase(S[p].find(a[T[t]].lx));
		}
		ANS.erase(ANS.find(a[T[t]].ans));
		if(!flg){flg=1;if(k)inserta(T[t],dfn[y],k);}
		else if(k)insertc(T[t],dfn[y],k);
		if(p){
			S[p].insert(a[T[t]].lx);
			k=(*S[p].rbegin())-pre;
			ANS.erase(ANS.find(a[T[top[p]]].ans));
			fresh(T[top[p]],dfn[p],p);
			ANS.insert(a[T[top[p]]].ans);
		}
		ANS.insert(a[T[t]].ans);
	}
}
char ss[3];
struct qnode{int u,v,w;}q[N];
int main()
{
	int n,m,i,u,v;
	n=gi();m=gi();
	for(i=1;i<n;i++){u=gi();v=gi();adde(u,v);}
	dfs1(1);top[1]=1;dfs2(1);
	build(T[1],1,lim[1]);
	for(i=1;i<=m;i++){
		scanf("%s",ss);
		if(ss[0]=='+'){
			q[i].u=gi();q[i].v=gi();q[i].w=gi();
			modify(q[i].u,q[i].v,q[i].w);
		}
		else{
			int t=gi();
			modify(q[t].u,q[t].v,-q[t].w);
		}
		printf("%lld\n",*ANS.rbegin());
	}
}

感谢Freopen大佬的耐心讲解与答疑