9.26-CSP-S模拟12

T1 开挂

比较水的贪心题,可以证明一定只包含不相交,拿个栈就很好做了。

点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn=1e6+10;

int n,a[maxn],b[maxn];
vector<int>ned,spt;

void solve(){
	cin>>n;Rep(i,1,n)cin>>a[i];Rep(i,1,n)cin>>b[i];
	sort(a+1,a+n+1);
	Rep(i,2,n){
		if(a[i]==a[i-1]){
			ned.push_back(a[i]);
		}else {
			for(int j=a[i-1]+1;j<a[i] && (!ned.empty());++j){
				spt.push_back(j-ned.back());ned.pop_back();
			}
		}
	}
	int now=a[n]+1;
	while(!ned.empty()){ spt.push_back(now-ned.back());ned.pop_back();++now; }
	sort(b+1,b+n+1);
	ull ans=0;
	if(!spt.empty()){
		sort(spt.begin(),spt.end());
		int it=1;
		Dwn(i,spt.size()-1,0){
			ans+=1LL*b[it]*spt[i];
			++it;
		}
	}
	cout<<ans<<"\n";
}

int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }


T2 叁仟柒佰万

比较水的dp题,可以处理出最后一个段以i结尾时,前i个数分为若干段的方案数,显然是求出一个最大的左端点使得区间mex恰好达到要求,左端点之前的所有位置都可以转移过来,维护前缀和就行。

点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn=3.7e7+10,Mod=1e9+7;
const ll B=262143;

#define gc if(++ip==ie)fread(ip=buf,1,SZ,stdin)
const int SZ=1<<21;char buf[SZ],*ie=buf+SZ,*ip=ie-1;
inline int read(){ gc;while(*ip<'-')gc; bool f=*ip=='-';if(f)gc; int x=*ip&15;gc; while(*ip>'-'){x*=10;x+=*ip&15;gc;} return f ? -x : x; }

int tex,n;
int a[maxn];
int Mex,vis[300000],AMex;
int	f[maxn],L[maxn];

void solve(){
	n=read();
	Mex=AMex=0;
	if(n==37000000){
		int x,y;x=read(),y=read();
		a[1]=0;Rep(i,2,n)a[i]=(1LL*a[i-1]*x+y+i)&B;
	}else Rep(i,1,n)a[i]=read();
	Rep(i,1,n){ ++vis[a[i]]; while(vis[Mex])++Mex; }
	Rep(i,1,n)vis[a[i]]=f[i]=0;
	AMex=Mex,Mex=0;
	int ED=0;
	Dwn(i,n,1){
		++vis[a[i]];
		while(vis[Mex])++Mex;
		if(Mex==AMex){ ED=i;break; }
	}
	Rep(i,ED,n)vis[a[i]]=0;Mex=0;
	int l=1,ans=0;
	f[0]=1;
	for(int i=1;i<ED;++i){
		++vis[a[i]];
		while(vis[Mex])++Mex;
		if(Mex==AMex){
			while(l<i && (a[l]>Mex || (vis[a[l]]>1))){--vis[a[l]];++l;}
			L[i]=l;
		}else L[i]=-1;
		if(L[i]!=-1){ f[i]=f[L[i]-1];ans=(ans+f[i])%Mod; }
		f[i]=(f[i]+f[i-1])%Mod;
	}
	Rep(i,1,n)vis[a[i]]=0;
	cout<<(ans+1)%Mod<<"\n";

}

int main (){ tex=read();while(tex--)solve();return 0; }

T3 超级加倍

用了笛卡尔树,建出笛卡尔树使得两点树上路径的最大/小值为笛卡尔树上两点的lca,于是在大根/小根笛卡尔树上同时互为祖先关系的点对就是要求的路径数量,求这样的点对就是将一棵树转为dfs序,然后搜另一棵树,用BIT维护当前点到根的路径上出现了哪些dfs序,然后在BIT上查原来有几个点在它子树内就行。

点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn=2e6+10;

int n;
ll ans;
int a[maxn];

struct BIT{
	int c[maxn];
	#define lowbit(x) (x&-x)
	void Add(int x,int d){ for(;x<=n;x+=lowbit(x))c[x]+=d; }
	int Query(int x){ 
		int res=0;for(;x;x-=lowbit(x))res+=c[x];return res; 
	}
	int Range(int x,int y){ return Query(y)-Query(x-1); }
}B;

struct Graph{
	struct eg{int from,to,next;}e[maxn*2];
	int len,head[maxn];int f[maxn];int fa[maxn];bool vis[maxn];
	void lqx(int from,int to)
	{ e[++len].from=from,e[len].to=to,e[len].next=head[from],head[from]=len; }
}G;
struct CTT{
	struct eg{int from,to,next;}e[maxn*2];
	int f[maxn];
	int Find(int x){return f[x]==x ? x : f[x]=Find(f[x]);}
	void Init(){ Rep(i,1,n)f[i]=i; }
	int len,head[maxn];
	void lqx(int from,int to)
	{ e[++len].from=from,e[len].to=to,e[len].next=head[from],head[from]=len; }
	void Build1(){
		Init();
		Rep(u,1,n){
			for(int i=G.head[u];i;i=G.e[i].next){
				int v=G.e[i].to;
				if(v<u){
					if(Find(u)==Find(v))continue;
					lqx(Find(u),Find(v));
					f[Find(v)]=Find(u);
				}
			}
		}
	}
	void Build2(){
		Init();
		Dwn(u,n,1){
			for(int i=G.head[u];i;i=G.e[i].next){
				int v=G.e[i].to;
				if(v>u){
					if(Find(u)==Find(v))continue;
					lqx(Find(u),Find(v));
					f[Find(v)]=Find(u);
				}
			}
		}
	}
}T,E;

int st[maxn],ed[maxn],Te;

void Dfs1(int u){ 
	st[u]=++Te;
	for(int i=T.head[u];i;i=T.e[i].next){ int v=T.e[i].to; Dfs1(v); } ed[u]=Te; 
}
void Dfs2(int u){
	ans+=B.Range(st[u],ed[u]);
	B.Add(st[u],1);
	for(int i=E.head[u];i;i=E.e[i].next){ int v=E.e[i].to; Dfs2(v); }
	B.Add(st[u],-1);
}

void solve(){
	cin>>n;int x;
	Rep(i,1,n){ cin>>x;if(x)G.lqx(i,x),G.lqx(x,i); }
	T.Build1(),E.Build2();
	Dfs1(n),Dfs2(1);
	cout<<ans<<"\n";
}

int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }


T4 欢乐豆

提取子图最短路。

如果没有特殊边,那么显然每个源到的所有点最短路都是源的点权。所以当一个点没有被特殊边涉及时,直接如此求即可。对于被特殊边涉及的点,点数规模较小,他们通过特殊边相连构成若干联通块,对于每个块可以求出块内全源最短路,然后算总和。
路径类型分为

  • 块内 \(\rightarrow\) 块内
  • 块内 \(\rightarrow\) 块外
  • 块内 \(\rightarrow\) 块外 \(\rightarrow\) 块内

前两种就普通dij,用线段树模拟,第三种显然只能是在块内走一段,然后走到点权最小的一个块外点再走回来,算答案的时候特殊考虑一下就行

点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=(int)b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=(int)b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
#define int ll
using namespace std;

const int maxn=1e5+10,INF=1e17+20051107;

int n,m;
ll ans;
int a[maxn],id[maxn],ind;
multiset<int>MS;
vector<int>pts[maxn];
vector<pii>vec[maxn];
int ud[maxn],dis[maxn];

struct UN{
	int f[maxn],siz[maxn];
	void Init(int n){ Rep(i,1,n)f[i]=i,siz[i]=1; }
	int Find(int x){ return f[x]==x ? x : f[x]=Find(f[x]); }
	void Merge(int x,int y){ x=Find(x),y=Find(y); if(x==y)return; f[y]=x,siz[x]+=siz[y]; }
}S;

struct Seg{
	struct Tree{ int mini,pos,lazy,tag; }tr[maxn<<2];
	void Pushup(int rt){
		tr[rt].mini=min(tr[rt<<1].mini,tr[rt<<1|1].mini);
		if(tr[rt].mini==tr[rt<<1].mini &&(!tr[rt<<1].tag))tr[rt].pos=tr[rt<<1].pos;
		else tr[rt].pos=tr[rt<<1|1].pos;
		tr[rt].tag=tr[rt<<1].tag&tr[rt<<1|1].tag;
	}
	void Build(int rt,int l,int r){
		tr[rt].mini=tr[rt].lazy=INF;tr[rt].tag=0,tr[rt].pos=l;
		if(l==r)return;
		int mid=(l+r)>>1;
		Build(rt<<1,l,mid),Build(rt<<1|1,mid+1,r);
	}
	void Update(int rt,int w){tr[rt].mini=min(tr[rt].mini,w),tr[rt].lazy=min(tr[rt].lazy,w);}
	void Pushdown(int rt){
		if(tr[rt].lazy==INF)return;
		if(!tr[rt<<1].tag)Update(rt<<1,tr[rt].lazy);
		if(!tr[rt<<1|1].tag)Update(rt<<1|1,tr[rt].lazy);
		tr[rt].lazy=INF;
	}
	void Delete(int rt,int l,int r,int x){
		if(l==r)return tr[rt].mini=INF,tr[rt].tag=1,void();
		int mid=(l+r)>>1;Pushdown(rt);
		if(x<=mid)Delete(rt<<1,l,mid,x);
		else Delete(rt<<1|1,mid+1,r,x);
		Pushup(rt);
	}
	void Modify(int rt,int l,int r,int s,int t,int w){
		if(tr[rt].tag || t<s)return;
		if(s<=l && t>=r)return Update(rt,w);
		int mid=(l+r)>>1;Pushdown(rt);
		if(s<=mid)Modify(rt<<1,l,mid,s,t,w);
		if(t>mid)Modify(rt<<1|1,mid+1,r,s,t,w);
		Pushup(rt);
	}
}T;

void Dij(int i,int u){
	T.Build(1,1,pts[i].size());
	T.Modify(1,1,pts[i].size(),ud[u],ud[u],0);
	dis[ud[u]]=0;
	while(T.tr[1].mini!=INF){
		int x=T.tr[1].pos,dd=T.tr[1].mini,last=1;
		dis[x]=dd;T.Delete(1,1,pts[i].size(),x);
		x=pts[i][x-1];
		for(auto it : vec[x]){
			T.Modify(1,1,pts[i].size(),last,ud[it.fir]-1,a[x]+dd);
			T.Modify(1,1,pts[i].size(),ud[it.fir],ud[it.fir],dd+it.sec);
			last=ud[it.fir]+1;
		}
		T.Modify(1,1,pts[i].size(),last,pts[i].size(),dd+a[x]);
	}
}

void solve(){
	cin>>n>>m;Rep(i,1,n)cin>>a[i],MS.insert(a[i]);
	int x,y,w;S.Init(n);
	Rep(i,1,m){ cin>>x>>y>>w;vec[x].emplace_back(y,w);S.Merge(x,y); }
	Rep(i,1,n){
		sort(vec[i].begin(),vec[i].end());
		if(S.Find(i)==i && S.siz[i]>1)id[i]=++ind;
	}
	Rep(i,1,n){
		if(S.siz[S.Find(i)]>1)pts[id[S.Find(i)]].push_back(i);
		else ans+=(n-1)*a[i];
	}
	Rep(i,1,ind){
		for(int j=0;j<(int)pts[i].size();++j){ ud[pts[i][j]]=j+1;MS.erase(MS.find(a[pts[i][j]])); }
		int tmp= MS.empty() ? INF : (*MS.begin());
		for(auto u : pts[i]){
			Dij(i,u);
			int mini=INF;
			Rep(j,1,pts[i].size())mini=min(mini,dis[j]+a[pts[i][j-1]]);
			Rep(j,1,pts[i].size())ans+=min(dis[j],mini+tmp);
			ans+=mini*(n-pts[i].size());
		}
		for(auto u : pts[i])MS.insert(a[u]);
	}
	cout<<ans<<"\n";
}

#undef int
int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }

posted @ 2022-09-26 18:47  Delov  阅读(45)  评论(0编辑  收藏  举报