230925校内赛

T1 开挂

我卢本伟没有开挂

pPbEt6U.jpg

题解

挺简单的,不过我写的比较麻烦

因为我们需要让多的尽可能多来让少的尽可能少,所以会想到用栈来存储需要更改的数,

靠近栈底就需要更多次数来更改,栈顶则更少

最后只用记录下来所有的次数并按从多到少依次分配从小到大的修改代价

#include<bits/stdc++.h>
#define int unsigned long long
#define N 1000010
using namespace std;
int n,a[N],b[N],ans;
vector<int>g;
stack<int>stk;
bool cmp(int x,int y){
	return x>y;
}
signed main(){
	freopen("openhook.in","r",stdin);
	freopen("openhook.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i = 1;i<=n;i++)
		cin>>a[i];
	bool flag = true;
	sort(a+1,a+n+1);
	for(int i = 1;i<=n;i++)
		if(a[i]==a[i-1]) flag = false;
	if(flag){
		cout<<"0";
		return 0;
	}
	for(int i = 1;i<=n;i++)
		cin>>b[i];
	sort(b+1,b+n+1);
	for(int i = 1;i<=n;i++){
		if(a[i]==a[i-1]) stk.push(a[i]);
		else{
			int tmp = a[i-1]+1;
			while(stk.size()&&tmp!=a[i]){
				g.push_back(tmp-stk.top());
				stk.pop();tmp++;
			}
		}
	}
	int tmp = a[n]+1;
	while(stk.size()){
		g.push_back(tmp-stk.top());
		stk.pop();tmp++;
	}
	sort(g.begin(),g.end(),cmp);
	for(int i = 0;i<g.size();i++)
		ans+=b[i+1]*g[i];
	cout<<ans;
	return 0;
}

T2 叁仟柒佰万

陈刀仔!

pPbELng.jpg

题解

\(dp\) 题,设 \(f_i\) 表示以 \(i\) 为分界点的方案数有转移方程式别问为什么,问就是不会

\(f_i = \sum f_j [mex([i,j])==K]\)

对于同一个右端点其对应的左端点的 \(mex\) 是单调的,我们可以直接用一个双指针+桶,维护区间的 \(mex\) 是否为 \(K\)

时间复杂度 \(\mathcal O(n)\)

#include<bits/stdc++.h>
#define mod 1000000007
#define N 37000010
using namespace std;
int n,a[N],f[N],sum[N];
int main(){
	freopen("clods.in","r",stdin);
	freopen("clods.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T;
	cin>>T;
	while(T--){
		cin>>n;
		for(int i = 0;i<=n;i++)
			f[i] = 0;
		if(n!=N-10){
			for(int i = 1;i<=n;i++){
				cin>>a[i];
				sum[i] = 0;
			}
		}else{
			int x,y;
			cin>>x>>y;
			for(int i = 0;i<=262144;i++)
				sum[i] = 0;
			for(int i = 2;i<=n;i++)
				a[i] = (1ll*a[i-1]*x+y+i)&262143;
		}
		for(int i = 2;i<=n;i++)
			sum[a[i]] = 1;
		int tmp = 0;
		while(sum[tmp]) tmp++;
		int s = 0;
		for(int i = 0;i<=n;i++)
			sum[i] = 0;
		int p = 1;
		if(tmp){
			for(int i = 1;i<=n;i++){
				if(a[i]<=tmp){
					if(!sum[a[i]]) s++;
					sum[a[i]]++;
					if(s==tmp){
						p = i;
						break;
					}
				}
			}
		}
		int o = 1;
		s = 1;
		for(int i = p;i<=n;i++){
			if(i^p) sum[a[i]]++;
			while(o<i&&(a[o]>tmp||sum[a[o]]>1)){
				sum[a[o]]--;
				s = (s+f[o])%mod;
				o++;
			}
			f[i] = s;
		}
		cout<<f[n]<<"\n";
	}
	return 0;
}

T3 超级加倍

pPbZEM8.jpg

题解

我们先思考对于链的情况

假设我们选出的 \(x\)\(y\) 左边,可以维护 \(L_i,R_i\) 表示 \(i\) 左边第一个 $ > p_i$ 的数,以及 \(i\) 右边第一 个 \(<p_i\) 的数可以单调栈预处理

两个点合法当且仅当 \(R_x>y\)\(L_y<x\),可以看成一个三维偏序问题做到 \(\mathcal O(n\log^2n)\)

但是可以忽略 \(x<y\) 这个条件, 这样会把所有的 \(x>y\) 统计为合法,最后减去即可, 时间复杂 度 \(\mathcal O(n\log n)\)

我们再思考对于树的问题

可以考虑点分治,每次处理过分治中心的点对, 一个点合法需要满足是到根的最小值或最大值,另外需要求出路径最小值或最大值来和路径的另外一边合并

合并时仍存在二维偏序的限制,必定会带一 个 \(\log\), 无法优化, 时间复杂度 \(\mathcal O(n\log^2n)\)

对比前面两个算法发现毫不相关, 且点分治本身并没有利用到太多性质

再来看一看前面的东西,\(R_x>y\) 这类性质可以联想到笛卡尔树, 而笛卡尔树在树上的一个很类似的形态是 \(Kruskal\) 重构树

按点权从小到大建树建成 \(T_1\),从大到小建成 \(T_2\) (满足任意两点 \(x,y\)\(T_1\) 中的 \(lca\) 是路径最小值, 在 \(T_2\) 中是路径最大值)

以上部分可以用并查集实现, 那么两点 \(x,y\) 满足条件当且仅当 \(x\)\(T_1\) 中是 \(y\) 的祖先, \(y\)\(T_2\) 中是 \(x\) 的祖先

这是一个朴素的偏序问题, 在 \(T_1\) 中求出 \(DFS\) 序, 在 \(T_2\)\(DFS\), 用一个树状数组维护可行答案

时间复杂度 \(\mathcal O(n \log n)\),由于复杂度瓶颈在树状数组,常数非常小

#include<bits/stdc++.h>
#define N 2000010
using namespace std;
int n,tim,cnt,p[N],h[N],dfn[N],t[N],siz[N];
long long ans;
struct edge{
	int v,ne;
}e[N<<2];
struct tree{
	edge e1[N];
	int cnt,h1[N],fa[N];
	bool vis[N];
	int find_(int x){
		return x==fa[x]?x:fa[x] = find_(fa[x]);
	}
	void add(int u,int v){
		e1[++cnt].v = v;
		e1[cnt].ne = h1[u];
		h1[u] = cnt;
	}
	void build(){
		for(int i = 1;i<=n;i++) fa[i] = i;
		for(int i = 1;i<=n;i++){
			vis[p[i]] = 1;
			for(int j = h[p[i]];j;j = e[j].ne){
				int v = e[j].v;
				if(vis[v]){
					v = find_(v);
					add(p[i],v);
					fa[v] = p[i];
				}
			}
		}
	}
}T1,T2;
void add(int u,int v){
	e[++cnt].v = v;
	e[cnt].ne = h[u];
	h[u] = cnt;
}
int ask(int x){
	int res = 0;
	for(;x;x-=x&(-x))
		res+=t[x];
	return res;
}
void change(int x,int v){
	for(;x<=n;x+=x&(-x))
		t[x]+=v;
}
void dfs(int x){
	dfn[x] = ++tim;
	siz[x] = 1;
	for(int i = T1.h1[x];i;i = T1.e1[i].ne){
		dfs(T1.e1[i].v);
		siz[x]+=siz[T1.e1[i].v];
	}
}
void calc(int x){
	ans+=ask(dfn[x]+siz[x]-1)-ask(dfn[x]);
	change(dfn[x],1);
	for(int i = T2.h1[x];i;i = T2.e1[i].ne)
		calc(T2.e1[i].v);
	change(dfn[x],-1);
}
signed main(){
	freopen("charity.in","r",stdin);
	freopen("charity.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i = 1;i<=n;i++)
		p[i] = n-i+1;
	for(int i = 1;i<=n;i++){
		int x;
		cin>>x;
		if(x){
			add(x,i);add(i,x);
		}
	}
	T1.build();
	reverse(p+1,p+n+1);
	T2.build();
	dfs(1);
	calc(n);
	cout<<ans;
	return 0;
}

T4 欢乐豆

你们觉得我像是会的样子吗?

pPbDAqU.jpg

题解

pPbDuGR.jpg

#include<bits/stdc++.h>
#define re register
using namespace std;
char rbuf[20000002];
int pt=-1;
inline int read(){
	re int t=0;re char v=rbuf[++pt];
	while(v<'0')v=rbuf[++pt];
	while(v>='0')t=(t<<3)+(t<<1)+v-48,v=rbuf[++pt];
	return t;
}
int mn[12002],pos[12002],n,m,head[100002],cnt,fa[100002],a[100002],tg[12002],inf=1e9,X,Y,Z,sum[12002],dis[3002],pp[100002],pmn[100002],smn[100002];
struct edge{int to,next,w;}e[3002];
struct node{int x,y;bool operator <(const node A)const{return x<A.x;};};
long long ans;
inline int root(re int x){return x==fa[x]?x:fa[x]=root(fa[x]);}
vector<int>v[100002];
vector<node>E[3002];
inline void pu(re int p){
	if(!sum[p]){mn[p]=inf;return;}
	if(mn[p<<1]<mn[p<<1|1])mn[p]=mn[p<<1],pos[p]=pos[p<<1];
	else mn[p]=mn[p<<1|1],pos[p]=pos[p<<1|1];
}
inline void CG(re int x,re int y){
	if(!sum[x])return;
	tg[x]=min(tg[x],y);
	mn[x]=min(mn[x],y);
}
inline void pd(re int p){
	if(tg[p]^inf){
		CG(p<<1,tg[p]),CG(p<<1|1,tg[p]);
		tg[p]=inf;
	}
}
inline void del(re int p,re int l,re int r){
	--sum[p];
	if(l==r){pos[p]=0,mn[p]=inf;return;}
	pd(p);
	re int mid=l+r>>1;
	if(X<=mid)del(p<<1,l,mid);
	else del(p<<1|1,mid+1,r);
	pu(p);
}
inline void cg(re int p,re int l,re int r){
	if(!sum[p])return;
	if(l>=X&&r<=Y)return CG(p,Z);
	pd(p);
	re int mid=l+r>>1;
	if(X<=mid)cg(p<<1,l,mid);
	if(Y>mid)cg(p<<1|1,mid+1,r);
	pu(p);
}
inline void build(re int p,re int l,re int r){
	sum[p]=r-l+1,tg[p]=inf;
	if(l==r){mn[p]=inf-1,pos[p]=l;return;}
	re int mid=l+r>>1;
	build(p<<1,l,mid),build(p<<1|1,mid+1,r);
	pu(p);
}
inline void work(re int x){
	for(re int i=0;i<v[x].size();++i)pp[v[x][i]]=i+1;
	re int S=v[x].size();
	for(re int i=1;i<=S;++i)E[i].clear();
	for(re int i=0;i<S;++i)for(re int j=head[v[x][i]];j;j=e[j].next)E[i+1].push_back((node){pp[e[j].to],e[j].w});
	for(re int i=1;i<=S;++i)sort(E[i].begin(),E[i].end());
	for(re int i=1;i<=S;++i){
		build(1,1,S),X=Y=i,Z=0,cg(1,1,S);
		for(re int j=1;j<=S;++j){
			re int z=pos[1];dis[z]=mn[1];
			X=z,del(1,1,S);
			re int lst=0;
			for(re int k=0;k<E[z].size();++k){
				if(E[z][k].x-1>lst)X=lst+1,Y=E[z][k].x-1,Z=dis[z]+a[v[x][z-1]],cg(1,1,S);
				X=Y=E[z][k].x,Z=dis[z]+min(a[v[x][z-1]]+min(pmn[x-1],smn[x+1]),E[z][k].y),cg(1,1,S),lst=E[z][k].x;
			}
			if(lst^S)X=lst+1,Y=S,Z=dis[z]+a[v[x][z-1]],cg(1,1,S);
		}
		re int mn=a[v[x][i-1]];
		for(re int j=1;j<=S;++j)if(j^i)ans+=dis[j],mn=min(mn,dis[j]+a[v[x][j-1]]);
		ans+=1ll*mn*(n-v[x].size());
	}
}
signed main(){
	freopen("happybean.in","r",stdin);
	freopen("happybean.out","w",stdout);
	fread(rbuf,1,20000000,stdin);
	n=read(),m=read();
	for(re int i=1;i<=n;++i)a[i]=read(),fa[i]=i;
	for(re int i=1,x,y,z;i<=m;++i){
		x=read(),y=read(),z=read();
		e[++cnt]=(edge){y,head[x],z},head[x]=cnt;
		fa[root(x)]=root(y);
	}
	for(re int i=1;i<=n;++i)v[root(i)].push_back(i);
	pmn[0]=inf;
	for(re int i=1;i<=n;++i){
		pmn[i]=pmn[i-1];
		if(i==root(i))for(re int j=0;j<v[i].size();++j)pmn[i]=min(pmn[i],a[v[i][j]]);
	}
	smn[n+1]=inf;
	for(re int i=n;i;--i){
		smn[i]=smn[i+1];
		if(i==root(i))for(re int j=0;j<v[i].size();++j)smn[i]=min(smn[i],a[v[i][j]]);
	}
	for(re int i=1;i<=n;++i)if(i==root(i))work(i);
	printf("%lld",ans);
}

posted @ 2023-09-28 15:39  cztq  阅读(13)  评论(0编辑  收藏  举报