百度之星2023决赛简要题解

前言

本人考试前一直在学文化,疏于训练,再加上考试时感冒头晕发挥不佳,只切了 ABCDH ,成功打铁。看来该加训了!

A 找矩阵

对每个点算出上下左右能到达的最远点,直接枚举 S 在方框的哪一条边然后枚举对边直接判断就好了。有一些细节需要注意一下。

#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
I love Elaina;
const int N=3010;
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int n,m,L[N][N],R[N][N],U[N][N],D[N][N],l1,r1,l2,r2,tl,tr;
char mp[N][N],tmp[N][N];
pair<int,int>pos;
void solve(){
	memset(L,0x3f,sizeof(L)),memset(R,-1,sizeof(R)),memset(U,0x3f,sizeof(U)),memset(D,-1,sizeof(D));
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(mp[i][j]=='S')pos=make_pair(i,j);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(mp[i][j]=='#')continue;
			L[i][j]=min(j,L[i][j-1]);
		}
		for(int j=m;j;j--){
			if(mp[i][j]=='#')continue;
			R[i][j]=max(j,R[i][j+1]);
		}
	}
	for(int j=1;j<=m;j++){
		for(int i=1;i<=n;i++){
			if(mp[i][j]=='#')continue;
			U[i][j]=min(i,U[i-1][j]);
		}
		for(int i=n;i;i--){
			if(mp[i][j]=='#')continue;
			D[i][j]=max(i,D[i+1][j]);
		}
	}
	for(int i=pos.first;i<=pos.first;i++){
		for(int j=pos.first+1;j<=n;j++){
			if(mp[j][pos.second]=='#')continue;
			l1=L[i][pos.second],r1=R[i][pos.second],l2=L[j][pos.second],r2=R[j][pos.second],tl=tr=-1;
			if(l1>=l2){
				for(int k=l1;k<=pos.second;k++){
					if(D[i][k]>=j){
						tl=k;
						break;
					}
				}
			}
			else{
				for(int k=l2;k<=pos.second;k++){
					if(U[j][k]<=i){
						tl=k;
						break;
					}
				}
			}
			if(r1<=r2){
				for(int k=r1;k>=pos.second;k--){
					if(D[i][k]>=j){
						tr=k;
						break;
					}
				}
			}
			else{
				for(int k=r2;k>=pos.second;k--){
					if(U[j][k]<=i){
						tr=k;
						break;
					}
				}
			}
			if(tl!=tr&&tl!=-1&&tr!=-1){
				puts("Yes");
				exit(0);
			}
		}
	}
	for(int i=1;i<pos.first;i++){
		for(int j=pos.first;j<=pos.first;j++){
			if(mp[i][pos.second]=='#')continue;
			l1=L[i][pos.second],r1=R[i][pos.second],l2=L[j][pos.second],r2=R[j][pos.second],tl=tr=-1;
			if(l1>=l2){
				for(int k=l1;k<=pos.second;k++){
					if(D[i][k]>=j){
						tl=k;
						break;
					}
				}
			}
			else{
				for(int k=l2;k<=pos.second;k++){
					if(U[j][k]<=i){
						tl=k;
						break;
					}
				}
			}
			if(r1<=r2){
				for(int k=r1;k>=pos.second;k--){
					if(D[i][k]>=j){
						tr=k;
						break;
					}
				}
			}
			else{
				for(int k=r2;k>=pos.second;k--){
					if(U[j][k]<=i){
						tr=k;
						break;
					}
				}
			}
			if(tl!=tr&&tl!=-1&&tr!=-1){
				puts("Yes");
				exit(0);
			}
		}
	}
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)scanf("%s",mp[i]+1);
	solve();
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)tmp[i][j]=mp[i][j];
	swap(n,m);
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)mp[i][j]=tmp[j][i];
	solve();
	puts("No");
	return 0;
}

B 错峰旅行

考虑离散化,用差分算出每一段不拥挤城市个数即可。

#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=5010;
const int M=1000010;
const ll mod=1e9+7;
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int n,m,s,t,k,x,L[M],R[M],pos[M<<1],cur,p,qwq,d[M<<1];
ll ans=1;
int main(){
	n=read(),m=read(),s=read(),t=read()+1;
	for(int i=1;i<=m;i++)x=read(),L[i]=pos[2*i-1]=read(),R[i]=pos[2*i]=read()+1;
	pos[2*m+1]=s,pos[2*m+2]=t;
	sort(pos+1,pos+2*m+3);
	k=unique(pos+1,pos+2*m+3)-pos-1;
	s=lower_bound(pos+1,pos+k+1,s)-pos,t=lower_bound(pos+1,pos+k+1,t)-pos;
	for(int i=1;i<=m;i++){
		L[i]=lower_bound(pos+1,pos+k+1,L[i])-pos,R[i]=lower_bound(pos+1,pos+k+1,R[i])-pos;
		d[L[i]]--,d[R[i]]++;
	}
	cur=n;
	for(int i=1;i<t;i++){
		cur+=d[i];
		if(i>=s){
			p=pos[i+1]-pos[i],qwq=cur;
			while(p){
				if(p&1)ans=1ll*ans*qwq%mod;
				qwq=1ll*qwq*qwq%mod,p>>=1;
			}
		}
	}
	printf("%lld",ans);
	return 0;
}

C 社交树

考虑将 \(a^{x+d}\) 拆成 \(a^{x-dep_u}\)\(a^{dep_v}\) ,把 \(a^{x-dep_u}\) 放线段树里面存,然后查询的时候再乘上 \(a^{dep_v}\) 就可以了。

#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=1e5+10;
const ll mod=1e8+7;
const int block=10000;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
ll n,m,A,op,x,y,head[N],cnt,siz[N],dfn[N],pos[N],p1[N],p2[N],idx,ans[N],dep[N];
struct edge{int to,nxt;}e[N<<1];
void addedge(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
ll qp(int p){return 1ll*p2[p/block]*p1[p%block]%mod;}
void dfs(int u,int fa){
	siz[u]=1,dfn[u]=++idx,pos[idx]=u,dep[u]=dep[fa]+1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa)continue;
		dfs(v,u);
		siz[u]+=siz[v];
	}
}
namespace AyaseEli{
	#define ls(k) (k<<1)
	#define rs(k) (k<<1|1)
	struct node{
		int l,r;
		ll val;
	}tree[N<<2];
	void pushdown(node *tree,int k){
		if(tree[k].val){
			tree[ls(k)].val=(tree[ls(k)].val+tree[k].val)%mod,tree[rs(k)].val=(tree[rs(k)].val+tree[k].val)%mod;
			tree[k].val=0;
		}
	}
	void build(node *tree,int k,int l,int r){
		tree[k].l=l,tree[k].r=r;
		if(l==r)return;
		int mid=(l+r)/2;
		build(tree,ls(k),l,mid),build(tree,rs(k),mid+1,r);
	}
	void change(node *tree,int k,int ql,int qr,ll v){
		if(ql<=tree[k].l&&tree[k].r<=qr){
			tree[k].val=(tree[k].val+v)%mod;
			return;
		}
		pushdown(tree,k);
		int mid=(tree[k].l+tree[k].r)/2;
		if(ql<=mid)change(tree,ls(k),ql,qr,v);
		if(mid<qr)change(tree,rs(k),ql,qr,v);
	}
	ll query(node *tree,int k,int q){
		if(tree[k].l==tree[k].r)return tree[k].val;
		pushdown(tree,k);
		int mid=(tree[k].l+tree[k].r)/2;
		if(q<=mid)return query(tree,ls(k),q);
		else return query(tree,rs(k),q);
	}
}
I love AyaseEli;
int main(){
	n=read(),m=read(),A=(read()+mod)%mod;
	p1[0]=p2[0]=1;
	for(int i=1;i<=block;i++)p1[i]=1ll*p1[i-1]*A%mod;
	for(int i=1;i<=block;i++)p2[i]=1ll*p2[i-1]*p1[block]%mod;
	for(int i=2;i<=n;i++){
		x=read();
		addedge(x,i),addedge(i,x);
	}
	dfs(1,0),build(tree,1,1,n);
	while(m--){
		op=read();
		if(op==1){
			x=read(),y=(read()+mod-1)%(mod-1);
			change(tree,1,dfn[x],dfn[x]+siz[x]-1,qp((y-dep[x]+mod-1)%(mod-1)));
		}
		if(op==2){
			x=read();
			printf("%lld\n",1ll*query(tree,1,dfn[x])*qp(dep[x])%mod);
		}
	}
	return 0;
}

D 传信游戏

反向连边,看看能从 0 走到哪些点就行了。

#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
I love Elaina;
const int N=1010;
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int n,a[N],ans;
vector<int>G[N];
void dfs(int u){
	ans^=u;
	for(int v:G[u])dfs(v);
}
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		G[a[i]].push_back(i);
	}
	dfs(0);
	printf("%d",ans);
	return 0;
}

E 喵喵卫士,全靠你了

考虑按层数从深往浅 dp 。我们设 \(f_{i,j,k}\) 表示当前填到第 i 层,还有 j 个点没有填,第 i 层填了 k 个点的方案数。转移方程很容易推。

#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=110;
const ll mod=1e9+7;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
ll n,a[N],f[N][N][N],p[N],cnt[N],sum[N],fac[N],ifac[N],C[N][N];
ll qp(ll x,ll p){
	ll res=1;
	while(p){
		if(p&1)res=1ll*res*x%mod;
		x=1ll*x*x%mod,p>>=1;
	}
	return res;
}
int main(){
	fac[0]=C[0][0]=1;
	for(int i=1;i<=100;i++)fac[i]=1ll*fac[i-1]*i%mod;
	ifac[100]=qp(fac[100],mod-2);
	for(int i=100;i;i--)ifac[i-1]=1ll*ifac[i]*i%mod;
	for(int i=1;i<=100;i++){
		C[i][0]=1;
		for(int j=1;j<=i;j++)C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
	}
	n=read();
	for(int i=1;i<=n;i++)a[i]=read(),cnt[a[i]]++;
	for(int i=n-1;i>=0;i--)sum[i]=sum[i+1]+cnt[i];
	for(int i=0;i<=n;i++)f[i][sum[i]][0]=1;
	for(int d=n-1;d>=0;d--)for(int i=0;i<=sum[d+1];i++)for(int j=0;j<=sum[d+1]-i;j++)for(int k=1;k<=cnt[d]+i;k++)f[d][i+cnt[d]-k][k]=(f[d][i+cnt[d]-k][k]+1ll*f[d+1][i][j]*fac[j+k-1]%mod*ifac[k-1]%mod*C[i+cnt[d]][k]%mod)%mod;
	printf("%lld",f[0][0][1]);
	return 0;
}

F 图论大师

直接求邻接矩阵的 k 次幂即可。

#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=110;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
ll n,m,q,x,y,z;
struct matrix1{
	ll n,m,a[N][N];
	matrix1(){memset(a,0x3f,sizeof(a));}
	matrix1 operator*(const matrix1 &rhs)const{
		matrix1 res;
		res.n=n,res.m=rhs.m;
		for(int k=1;k<=m;k++)for(int i=1;i<=n;i++)for(int j=1;j<=rhs.m;j++)res.a[i][j]=min(res.a[i][j],a[i][k]+rhs.a[k][j]);
		return res;
	}
}bit1[30],B1;
struct matrix2{
	ll n,m,a[N][N];
	matrix2(){memset(a,-0x3f,sizeof(a));}
	matrix2 operator*(const matrix2 &rhs)const{
		matrix2 res;
		res.n=n,res.m=rhs.m;
		for(int k=1;k<=m;k++)for(int i=1;i<=n;i++)for(int j=1;j<=rhs.m;j++)res.a[i][j]=max(res.a[i][j],a[i][k]+rhs.a[k][j]);
		return res;
	}
}bit2[30],B2;
int main(){
	n=read(),m=read(),q=read();
	bit1[0].n=bit1[0].m=bit2[0].n=bit2[0].m=B1.m=B2.m=n,B1.n=B2.n=1;
	for(int i=1;i<=m;i++){
		x=read(),y=read(),z=read();
		bit1[0].a[x][y]=min(bit1[0].a[x][y],z),bit1[0].a[y][x]=bit1[0].a[x][y];
		bit2[0].a[x][y]=max(bit2[0].a[x][y],z),bit2[0].a[y][x]=bit2[0].a[x][y];
	}
	for(int i=1;i<30;i++)bit1[i]=bit1[i-1]*bit1[i-1],bit2[i]=bit2[i-1]*bit2[i-1];
	while(q--){
		x=read(),y=read(),z=read();
		for(int i=1;i<=n;i++)B1.a[1][i]=0x3f3f3f3f3f3f3f3f,B2.a[1][i]=-0x3f3f3f3f3f3f3f3f;
		B1.a[1][x]=B2.a[1][x]=0;
		for(int i=0;i<30;i++)if(z&(1<<i))B1=B1*bit1[i],B2=B2*bit2[i];
		if(B2.a[1][y]<0)puts("-1 -1");
		else printf("%lld %lld\n",B1.a[1][y],B2.a[1][y]);
	}
	return 0;
}

G 这一击贯穿星辰

可以先通过背包求出分配 x 点体力时 \(A\times B\) 的最大值,以及 \(C\) 的最大值,这样会对应出 \(m\) 种可能的最优方案。注意到对于每一种方案,都会得到一条关于 \(D\) 的一次函数,这样我们用李超线段树或者其他例如分治、凸包之类的方法就可以求解了。

#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=3010;
const int M=300010;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int n,m,q,x,y,z;
ll f[4][N];
vector<pair<int,int> >qwq[3];
namespace AyaseEli{
	#define ls(k) (k<<1)
	#define rs(k) (k<<1|1)
	struct line{
		ll k,b;
		ll F(ll x){return 1ll*k*x+b;}
	}tree[M<<2];
	void modify(line *tree,int k,int l,int r,line v){
		if(l==r){
			if(tree[k].F(l)<v.F(l))tree[k]=v;
			return;
		}
		int mid=(l+r)/2;
		if(tree[k].F(mid)<v.F(mid))swap(tree[k],v);
		if(tree[k].F(l)<v.F(l))modify(tree,ls(k),l,mid,v);
		else modify(tree,rs(k),mid+1,r,v);
	}
	ll query(line *tree,int k,int l,int r,int q){
		ll res=tree[k].F(q);
		if(l==r)return tree[k].F(q);
		int mid=(l+r)/2;
		if(q<=mid)res=max(res,query(tree,ls(k),l,mid,q));
		else res=max(res,query(tree,rs(k),mid+1,r,q));
		return res;
	}
}
I love AyaseEli;
int main(){
	n=read(),m=read(),q=read();
	for(int i=1;i<=n;i++){
		x=read(),y=read()-1,z=read();
		qwq[y].push_back(make_pair(x,z));
	}
	for(int i=0;i<3;i++)for(pair<int,int>tmp:qwq[i])for(int j=m;j>=tmp.first;j--)f[i][j]=max(f[i][j],f[i][j-tmp.first]+tmp.second);
	for(int i=0;i<=m;i++)for(int j=0;j<=m-i;j++)f[3][i+j]=max(f[3][i+j],1ll*(100+f[0][i])*(100+f[1][j]));
	for(int i=0;i<=m;i++)modify(tree,1,0,3e5,(line){-f[3][i],1ll*f[3][i]*(100+f[2][m-i])});
	while(q--){printf("%lld\n",query(tree,1,0,3e5,read()));}
	return 0;
}

H 小度的双色球

看上去就很根号分治。对于小集合可以直接暴力使用 set 进行求解。对于大集合我们先预处理,先求出另外的集合使得里面的数出现次数都是超过枚举阈值的,然后直接对求出的新集合暴力枚举就行,复杂度可以通过计算不等式证明。调整块长,可以使得复杂度变成 \(O(n\sqrt{nlogn})\) ,如果对小集合使用一些哈希表之类的东西可以去掉 log 。

#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
I love Elaina;
const int N=200010;
const int block=150;
const int mod=998244353;
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int n,m,x,y,cnt,inv[N],ans[N<<4],id[N],len;
vector<int>rev[N],ts[N],qwq;
set<int>st[N];
int calc(int i,int j){return id[i]*len+id[j];}
int main(){
	n=read(),m=read();
	inv[1]=n;
	for(int i=2;i<=200000;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=1;i<=n;i++){
		x=read();
		while(x--){
			y=read();
			st[i].insert(y),rev[y].push_back(i);
		}
	}
	for(int i=1;i<=n;i++){
		if(rev[i].size()>block){
			qwq.push_back(i);
			for(int tmp:rev[i])ts[tmp].push_back(i);
		}
	}
	sort(qwq.begin(),qwq.end());
	len=qwq.size();
	for(int i=0;i<qwq.size();i++)id[qwq[i]]=i;
	for(int i=1;i<=n;i++){
		if(ts[i].size()<2)continue;
		for(int j=0;j<ts[i].size()-1;j++)for(int k=j+1;k<ts[i].size();k++)ans[calc(ts[i][j],ts[i][k])]++;
	}
	while(m--){
		x=read(),y=read();
		if(rev[x].size()>rev[y].size())swap(x,y);
		cnt=0;
		if(rev[x].size()<=block){for(int tmp:rev[x])if(*st[tmp].lower_bound(y)==y)cnt++;}
		else{
			if(x>y)swap(x,y);
			cnt=ans[calc(x,y)];
		}
		if(!cnt)puts("-1");
		else printf("%d\n",inv[cnt]);
	}
	return 0;
}

I 寻宝

二分答案,考虑网络流。感觉这题其实难点在想到用网络流,建模反而不难。

#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=510;
const int M=5010;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
ll n,m,p,a[N],b[N],c[N],d[N],x,y,l,r,mid,ans;
bool vis[N][N];
namespace AyaseEli{
	int s,t,head[N<<2],cnt=1,dep[N<<2];
	struct edge{
		int to,nxt;
		ll flow;
	}e[M<<2];
	void addedge(int u,int v,ll f){
		e[++cnt].to=v,e[cnt].nxt=head[u],e[cnt].flow=f,head[u]=cnt;
		e[++cnt].to=u,e[cnt].nxt=head[v],e[cnt].flow=0,head[v]=cnt;
	}
	bool bfs(){
		memset(dep,-1,sizeof(dep));
		queue<int>q;
		q.push(s);
		dep[s]=0;
		while(!q.empty()){
			int u=q.front();
			q.pop();
			for(int i=head[u];i;i=e[i].nxt){
				int v=e[i].to;
				if(dep[v]==-1&&e[i].flow>0){
					dep[v]=dep[u]+1;
					q.push(v);
				}
			}
		}
		return dep[t]!=-1;
	}
	ll dfs(int u,ll cur){
		if(u==t||!cur)return cur;
		ll tmp,sum=0;
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(e[i].flow>0&&dep[u]+1==dep[v]){
				tmp=dfs(v,min(cur-sum,e[i].flow));
				if(!tmp)dep[v]=-1;
				e[i].flow-=tmp,e[i^1].flow+=tmp,sum+=tmp;
				if(sum==cur)break;
			}
		}
		return sum;
	}
	ll Dinic(){
		ll maxflow=0;
		while(bfs())maxflow+=dfs(s,1e18);
		return maxflow;
	}
}
I love AyaseEli;
bool check(ll lim){
	cnt=1;
	memset(head,0,sizeof(head));
	for(int i=1;i<=n;i++){
		if(d[i]>mid){
			addedge(i,i+n,b[i]),addedge(i+n,i+2*n,a[i]);
			for(int j=1;j<=n;j++){
				if(i==j||!vis[i][j])continue;
				if(c[j]+d[j]<=mid)addedge(i+n,j+n,1e9);
			}
		}
		else if(c[i]+d[i]>mid)addedge(i,i+n,b[i]),addedge(i+n,i+2*n,a[i]);
		else addedge(i,i+n,0),addedge(i+n,i+2*n,a[i]);
		addedge(s,i,1e9),addedge(i+2*n,t,1e9);
	}
	return Dinic()<=p;
}
int main(){
	n=read(),m=read(),p=read();
	s=0,t=3*n+1;
	for(int i=1;i<=n;i++)a[i]=read(),b[i]=read(),c[i]=read(),d[i]=read();
	for(int i=1;i<=m;i++){
		x=read(),y=read();
		vis[x][y]=vis[y][x]=1;
	}
	l=0,r=ans=2e9;
	while(l<=r){
		mid=(l+r)/2;
		if(check(mid))r=mid-1,ans=mid;
		else l=mid+1;
	}
	printf("%lld",ans);
	return 0;
}

J 石子游戏

自行模拟容易看出这样一个结论:最大值个数为奇数时先手获胜。于是我们可以考虑用线段树维护。细节比较多,线段树可以考虑离散化后建普通线段树,也可以考虑建值域线段树。

#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=500010;
const ll mod=998244353;
const ll inv2=499122177;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
ll n,m,op[N],x[N],y[N],pos[N];
ll qp(ll x,ll p){
	ll res=1;
	while(p){
		if(p&1)res=1ll*res*x%mod;
		x=1ll*x*x%mod,p>>=1;
	}
	return res;
}
namespace AyaseEli{
	#define ls(k) (k<<1)
	#define rs(k) (k<<1|1)
	struct node{
		int l,r;
		ll sum,val,cnt;
	}tree[N<<2];
	void pushup(node *tree,int k){tree[k].sum=(1ll*tree[ls(k)].sum*tree[rs(k)].val%mod+tree[rs(k)].sum)%mod,tree[k].val=1ll*tree[ls(k)].val*tree[rs(k)].val%mod,tree[k].cnt=tree[ls(k)].cnt+tree[rs(k)].cnt;}
	void build(node *tree,int k,int l,int r){
		tree[k].l=l,tree[k].r=r,tree[k].val=1;
		if(l==r)return;
		int mid=(l+r)/2;
		build(tree,ls(k),l,mid),build(tree,rs(k),mid+1,r);
		pushup(tree,k);
	}
	void modify(node *tree,int k,int q,ll v){
		if(tree[k].l==tree[k].r){
			if(!tree[k].cnt)tree[k].cnt=1,tree[k].sum=inv2;
			tree[k].val=1ll*tree[k].val*qp(inv2,v)%mod;
			return;
		}
		int mid=(tree[k].l+tree[k].r)/2;
		if(q<=mid)modify(tree,ls(k),q,v);
		else modify(tree,rs(k),q,v);
		pushup(tree,k);
	}
	pair<ll,ll>query(node *tree,int k,int ql,int qr){
		pair<ll,ll>res,t1,t2;
		if(ql<=tree[k].l&&tree[k].r<=qr)return make_pair(tree[k].sum,tree[k].val);
		int mid=(tree[k].l+tree[k].r)/2;
		if(qr<=mid)return query(tree,ls(k),ql,qr);
		if(mid<ql)return query(tree,rs(k),ql,qr);
		t1=query(tree,ls(k),ql,qr),t2=query(tree,rs(k),ql,qr);
		res.first=(1ll*t1.first*t2.second%mod+t2.first)%mod,res.second=1ll*t1.second*t2.second%mod;
		return res;
	}
}
I love AyaseEli;
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		op[i]=read(),x[i]=read(),y[i]=read();
		if(op[i]==1)pos[++m]=x[i];
	}
	sort(pos+1,pos+m+1);
	m=unique(pos+1,pos+m+1)-pos-1;
	build(tree,1,1,m);
	for(int i=1;i<=n;i++){
		if(op[i]==1){
			x[i]=lower_bound(pos+1,pos+m+1,x[i])-pos;
			modify(tree,1,x[i],y[i]);
		}
		if(op[i]==2){
			x[i]=lower_bound(pos+1,pos+m+1,x[i])-pos,y[i]=upper_bound(pos+1,pos+m+1,y[i])-pos-1;
			printf("%lld\n",x[i]<=y[i]?query(tree,1,x[i],y[i]).first:0);
		}
	}
	return 0;
}

K 世界泡

阅读理解题。可以对于每一个点记录后手从该点出发的所有路径,直接 dsu on tree 就可以了。

#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=500010;
const int M=20;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
ll n,m,a[N],x,y,head[N],cnt,dis[N],diss[N],val[N],ans;
struct edge{int to,nxt;}e[N<<1];
bool vis[N];
priority_queue<ll>q[N];
void addedge(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
void dfs(int u,int fa){
	dis[u]=dis[fa]+a[u],val[u]=val[fa]*(1+vis[fa]),diss[u]=diss[fa]+a[u]*val[u];
	ll minn=0x3f3f3f3f;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa)continue;
		dfs(v,u);
		minn=min(minn,a[v]);
		if(q[u].size()<q[v].size())swap(q[u],q[v]);
		while(!q[v].empty())q[u].push(q[v].top()),q[v].pop();
	}
	while(!q[u].empty()&&(q[u].top()-diss[u])/(val[u]*(1+vis[u]))>=dis[u]-a[1]+minn)q[u].pop();
	if(!q[u].empty())ans=max(ans,(q[u].top()-diss[u])/(val[u]*(1+vis[u]))-dis[u]+a[1]);
	q[u].push(diss[u]);
}
int main(){
	val[0]=1;
	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=m;i++)vis[read()]=1;
	for(int i=1;i<n;i++){
		x=read(),y=read();
		addedge(x,y),addedge(y,x);
	}
	dfs(1,0);
	printf("%lld",ans);
	return 0;
}

L 树上魔法使

挺难的数学题。

#include<bits/stdc++.h>
#define I using
#define love namespace
#define Elaina std
#define ll long long
I love Elaina;
const int N=1000010;
const ll mod=998244353;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
ll n,m,x,y,head[N],cnt,ans,deg[N],p1[N],p2[N],p3[N],sum;
struct edge{int to,nxt;}e[N<<1];
void addedge(int u,int v){e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;}
ll qp(ll x,ll p){
	ll res=1;
	while(p){
		if(p&1)res=1ll*res*x%mod;
		x=1ll*x*x%mod,p>>=1;
	}
	return res;
}
int main(){
	p1[0]=p2[0]=p3[0]=1;
	n=read(),m=read();
	for(int i=1;i<=1000000;i++)p1[i]=1ll*p1[i-1]*m%mod;
	p2[1]=1ll*(m-1)*qp(m,mod-2)%mod,p3[1]=(2ll*p2[1]%mod-1+mod)%mod;
	for(int i=2;i<=1000000;i++)p2[i]=1ll*p2[i-1]*p2[1]%mod,p3[i]=1ll*p3[i-1]*p3[1]%mod;
	for(int i=1;i<n;i++){
		x=read(),y=read(),deg[x]++,deg[y]++;
		addedge(x,y),addedge(y,x);
	}
	ans=1ll*(n-1)*(n-1)%mod*p1[n-1]%mod;
	for(int i=1;i<=n;i++)sum=(sum+1-p2[deg[i]]+mod)%mod;
	ans=(ans+1ll*p1[n]*m%mod*sum%mod*sum%mod)%mod;
	for(int u=1;u<=n;u++){
		ans=(ans+2ll*(1-n+mod)*p1[n]%mod*(1-p2[deg[u]]+mod)%mod)%mod;
		ans=(ans+mod-1ll*p1[n]*m%mod*(1-p2[deg[u]]+mod)%mod*(1-p2[deg[u]]+mod)%mod)%mod;
		ans=(ans+1ll*p1[n]*(1-p2[deg[u]]+mod+m-1-1ll*m*p2[deg[u]]%mod+mod+1ll*(m-2)*(1ll*p2[1]*p3[deg[u]-1]%mod-p2[deg[u]]+mod)%mod)%mod)%mod;
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			ans=(ans+mod-1ll*p1[n]*m%mod*(1-p2[deg[u]]+mod)%mod*(1-p2[deg[v]]+mod)%mod)%mod;
			ans=(ans+p1[n-1])%mod;
			ans=(ans+1ll*p1[n-1]*(m-1)%mod*(1ll*(m-1)*(1-p2[deg[u]-1]+mod)%mod*(1-p2[deg[v]-1]+mod)%mod+1-p2[deg[u]-1]+mod+1-p2[deg[v]-1]+mod)%mod)%mod;
		}
	}
	printf("%lld",ans);
	return 0;
}

总结

这场比赛其实出的挺好的,很适合 OI 选手。希望我能知耻而后勇,在后续的省选等比赛中发挥出自己应有的水平,弥补自己一直以来的遗憾!

posted @ 2024-01-04 21:22  Skyjoy  阅读(1511)  评论(0)    收藏  举报
Live2D