21牛客多校第七场

A

(FMT)和一堆奇怪的东西 跑了

B

线段树 咕了

C

奇怪的题 好像和二进制有关 跑了

D

特征多项式 跑了

E

博弈推出结论+(FWT) 跑了

F

每个点在第二棵树上代表它的子树对应的(dfs)序对应区间,现在要在第一棵树上求一条由上至下的链使得这条链上所有点对应区间不相交

每个点(x)向上最远的限制即为(x)到根的路径上对应区间与(x)有重叠且深度最深的点,相当于一个区间染色问题

维护主席树表示一个点从根到这个点的所有点区间染色后的结果

这样很容易可以得到每个点向上的限制,和父亲的答案一起考虑即可得到这个点的答案

注意需要标记永久化,在每次(dfs)之后清空这个点的所有影响

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 300100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b)+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,nxt[MAXN<<1],to[MAXN<<1],fst[MAXN],cnt,in[MAXN],ou[MAXN],dfn,res[MAXN];
vector<int> G[MAXN];
void add(int u,int v){nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v;}
int ls[MAXN<<7],rs[MAXN<<7],mx[MAXN<<7],tag[MAXN<<7],tot,ans,rt[MAXN];
void mdf(int &k,int kk,int l,int r,int a,int b,int x)
{
	mx[k=++tot]=x,tag[k]=tag[kk],ls[k]=ls[kk],rs[k]=rs[kk];
	if(a<=l&&r<=b) {tag[k]=x;return ;}int mid=l+r>>1;
	if(a<=mid) mdf(ls[k],ls[kk],l,mid,a,b,x);
	if(b>mid) mdf(rs[k],rs[kk],mid+1,r,a,b,x);
}
int query(int k,int l,int r,int a,int b)
{
	if(!k) return 0;if(a<=l&&r<=b) return max(mx[k],tag[k]);
	int mid=l+r>>1,res=tag[k];
	if(a<=mid) res=max(res,query(ls[k],l,mid,a,b));
	if(b>mid) res=max(res,query(rs[k],mid+1,r,a,b));
	return res;
}
inline void del(int x)
{
	for(;tot>x;tot--) mx[tot]=tag[tot]=ls[tot]=rs[tot]=0;
}
void dfs(int x,int pa)
{
	in[x]=++dfn;for(auto v:G[x])
		if(v^pa) dfs(v,x);ou[x]=dfn;
}
void dfs(int x,int pa,int dep)
{
	int las=tot;res[x]=query(rt[pa],1,n,in[x],ou[x]);
	//cout<<x<<" "<<in[x]<<" "<<ou[x]<<" "<<res<<" "<<dep<<endl;
	res[x]=max(res[x],res[pa]),ans=max(ans,dep-res[x]);
	mdf(rt[x],rt[pa],1,n,in[x],ou[x],dep);
	ren if(to[i]^pa) dfs(to[i],x,dep+1);del(las);
}
int main()
{
	int a,b;rep(T,1,read())
	{
		n=read();dfn=ans=cnt=0;
		rep(i,2,n) a=read(),b=read(),add(a,b),add(b,a);
		rep(i,2,n) a=read(),b=read(),G[a].pb(b),G[b].pb(a);
		dfs(1,0);dfs(1,0,1);
		printf("%d
",ans);
		rep(i,1,n) fst[i]=rt[i]=res[i]=0,G[i].clear();
	}
}

G

奇妙分块 跑了

H

签到题,直接暴力枚举所有数的倍数,复杂度调和级数

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 1001001
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b)+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int lim=1e6;
int n,w[MAXN];
ll ans;
int main()
{
	n=read();rep(i,1,n) w[read()]++;
	rep(i,1,lim) if(w[i])
		for(int j=i;j<=lim;j+=i)
			ans+=1LL*w[i]*w[j]*w[j/i];
	printf("%lld
",ans);
}

I

签到题,两个均为(1)的位会有( imes2)的贡献,特判一下(0)

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b)+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int x,s,ans=1,flg=1;
int main()
{
	x=read(),s=read();
	dwn(i,30,0)
	{
		if(!((s>>i)&1))
		{
			if((x>>i)&1) return puts("0"),0;
		}
		else
		{
			if((x>>i)&1) ans*=2;
			else flg=0;
		}
	}
	printf("%d
",ans-flg);
}

J

先做(n)(dij)跑出所有正确的最短路

考虑对于错误(floyd)的最外层(i),若通过错误算法得到一些正确的(dis[i][j])

则原来通过这些正确(dis[i][j])直接转移得到的点依然是正确的,否则需要考虑转移的顺序

因为错误算法的第二层(j)是按升序转移到的,因此若正确转移出的(j'>j),则(j')可以再次转移,否则无法更新出正确答案

对于每个(i)来说,开一个以转移时间(j)为关键字的小根堆来记录转移,实现上述过程

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 300100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b)+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,v[2020][2020],nxt[5050],fst[2020],to[5050],val[5050],cnt;
int dis[2020][2020],vis[2020],ans;
void add(int u,int v,int w){nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v,val[cnt]=w;}
priority_queue<pii,vector<pii>,greater<pii> > q;
vector<pii> ok[2020];
void dij(int S)
{
	rep(i,1,n) dis[S][i]=inf,vis[i]=0;
	dis[S][S]=0;q.push({0,S});int x;
	while(!q.empty())
	{
		x=q.top().se;q.pop();if(vis[x]) continue;vis[x]=1;
		ren if(dis[S][to[i]]>dis[S][x]+val[i])
			dis[S][to[i]]=dis[S][x]+val[i],q.push({dis[S][to[i]],to[i]});
	}
	rep(i,1,n) if(S!=i&&(dis[S][i]!=inf)&&dis[S][i]==v[S][i])
		ok[S].pb({dis[S][i],i});
}
void work(int S)
{
	for(auto x:ok[S]) q.push({0,x.se});
	int w,x;while(!q.empty())
	{
		w=q.top().fi,x=q.top().se;q.pop();
		for(auto e:ok[x]) if(e.se>=w&&dis[S][e.se]!=v[S][e.se]&&dis[S][e.se]==v[S][x]+e.fi)
			v[S][e.se]=dis[S][e.se],q.push({e.se,e.se}),
			ok[S].pb({dis[S][e.se],e.se});
	}
}
int main()
{
	n=read(),m=read();rep(i,1,n) rep(j,1,n) if(i^j) v[i][j]=inf;
	int a,b,c;rep(i,1,m) a=read(),b=read(),c=read(),v[a][b]=c,add(a,b,c);
	rep(i,1,n) dij(i);rep(i,1,n) work(i);
	rep(i,1,n) rep(j,1,n) if(dis[i][j]==v[i][j]) ans++;
	printf("%d
",ans);
}

K

若没有取模,将原来的区间操作差分后相当于在一个位置(-1),后面(+1)

考虑原数组的差分数组(b),由于(sumlimits_{i=1}^{n+1}b_i=0)且前后正负一定可以抵消,答案即为(frac{1}{2}sumlimits_{i=1}^{n+1}|b_i|)

在取模的情况下,相当于对相同数量的(b_i+K)(b_i-K)后重新计算答案

显然只有当(b_i<0)(+K)才可能使答案减小,贡献为((K+b_i)-(-b_i)=K-2|b_i|)

(b_i>0)时同理可得贡献为(K-2|b_i|)

则对于一个询问区间,需要找到对答案贡献最大的(x)(|b_i|)计算

答案显然关于为(x)的下凸函数,可以三分得到答案

开两个权值主席树预处理所有的(|b_i|)可以快速得到最大的(x)(|b_i|)

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 200100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b)+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int lim=1<<30;
int n,a[MAXN],g[MAXN],q,cnt1[MAXN],cnt2[MAXN];
int rt1[MAXN],rt2[MAXN],w[MAXN<<7],ls[MAXN<<7],rs[MAXN<<7],tot;
ll sum[MAXN<<7],ans,pres[MAXN];
void mdf(int &k,int kk,int l,int r,int x)
{
	w[k=++tot]=w[kk]+1,sum[k]=sum[kk]+x,ls[k]=ls[kk],rs[k]=rs[kk];
	if(l==r) return ;int mid=l+r>>1;
	x<=mid?mdf(ls[k],ls[kk],l,mid,x):mdf(rs[k],rs[kk],mid+1,r,x);
}
ll query(int k,int kk,int l,int r,int x)
{
	if(!k) return 0LL;if(l==r) return 1LL*l*x;int mid=l+r>>1;
	if(x<=w[rs[k]]-w[rs[kk]]) return query(rs[k],rs[kk],mid+1,r,x);
	else return sum[rs[k]]-sum[rs[kk]]+query(ls[k],ls[kk],l,mid,x-w[rs[k]]+w[rs[kk]]);
}
int main()
{
	n=read(),q=read();rep(i,1,n) a[i]=read();
	rep(i,1,n) 
	{
		g[i]=a[i]-a[i-1],pres[i]=pres[i-1]+abs(g[i]);
		cnt1[i]=cnt1[i-1],cnt2[i]=cnt2[i-1];
		rt1[i]=rt1[i-1],rt2[i]=rt2[i-1];
		if(g[i]>0) cnt1[i]++,mdf(rt1[i],rt1[i-1],0,lim,g[i]);
		else cnt2[i]++,mdf(rt2[i],rt2[i-1],0,lim,-g[i]);
	}
	int x,y,l,r,ml,mr,r1,r2,k,c1,c2,tp=tot;ll bas,al,ar;
	while(q--)
	{
		x=read(),y=read(),k=read(),r1=rt1[y],r2=rt2[y];
		c1=cnt1[y]-cnt1[x]+1,c2=cnt2[y]-cnt2[x]+1;
		mdf(r1,r1,0,lim,a[x]);mdf(r2,r2,0,lim,a[y]);
		bas=(pres[y]-pres[x]+abs(a[x])+abs(a[y]))/2;ans=0;
		for(l=0,r=min(c1,c2);l<r;)
		{
			ml=l+r>>1,mr=ml+1;
			al=1LL*k*ml-query(r1,rt1[x],0,lim,ml)-query(r2,rt2[x],0,lim,ml);
			ar=1LL*k*mr-query(r1,rt1[x],0,lim,mr)-query(r2,rt2[x],0,lim,mr);
			ans=min(min(al,ar),ans);
			if(al<ar) r=mr-1;else l=ml+1;
		}
		printf("%lld
",ans+bas);
	}
}
原文地址:https://www.cnblogs.com/yyc-jack-0920/p/15116466.html