『模拟赛』CSP提高组模拟1

Rank

image


A.最短路

洛谷原题 [USACO09DEC] Cow Toll Paths G

一眼 Floyd,但没眼出来要跑好几遍。。

赛时思路上是开一个二维结构体存最短路和路上最大点权,判断若当前未联通或联通但总花费高于更新后总花费就更新。

没有考虑到一种较极限的情况:假设目标起终点为 \(i\)\(j\),中间点为 \(k\)\(i\)\(j\) 路径长任意,最大点权为 \(101\)\(j\)\(k\) 有两条待选路径,一条边权为 \(100\),点权为 \(1\),另一条边权为 \(2\) ,点权为 \(101\),显然选后一条路对 \(i\)\(j\) 更优,但根据我们的更新标准,\(k\)\(j\) 更新的是第一条,一次更新无法找到 \(i\)\(j\) 的最优路径。

所以简单的解决办法是多跑几遍。

考虑正解,发现上述方法错误的原因在于点权的判断,思考一下得到先更新点权小的点就不会造成更新紊乱的情况。所以先对点权排序并记录原序列,Floyd 跑的 \(i\)\(j\)\(k\) 替换为记录的原序列中的位置。

距离 \(dis\) 转移方程为:

\[dis_{i,j}=min(dis_{i,j},dis_{i,k}+dis_{k,j}) \]

总答案转移方程为:

\[ans_{i,j}=\min(ans_{i,j},dis_{i,j}+\max (maxv_{i,k},maxv_{k,j})) \]

赛时 code($80pts$)

这个想过就复制几遍 Floyd 部分就行了

赛时唐了,以为 0x3f3f3f3f 会出问题,忘了开的是 long long 了,然后就初始化的 -1。

#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(int (x)=(y);(x)>=(z);(x)--)
#define fuck printf(";;;;;;;;\n");
using namespace std;
typedef long long ll;
#define lx int
inline lx qr()
{
	char ch=getchar();lx x=0,f=1;
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
	return x*f;
}
#undef lx
#define qr qr()
const int Ratio=0;
const int N=1e4+5;
int n,m,q;
int v[305];
struct rmm
{
	ll d=-1;
	ll maxx=-1;
}dh[305][305];
int qs[N],qt[N];
namespace Wisadel
{
	bool Wck(int x,int y,int z)
	{
		if(dh[y][z].d!=-1&&dh[x][z].d!=-1)
		{
			if(dh[x][y].d==-1) return 1;
			else if(dh[x][y].d+dh[x][y].maxx>dh[y][z].d+dh[z][x].d+max(dh[y][z].maxx,dh[z][x].maxx))
				return 1;
		}
		return 0;
	}
	short main()
	{
		freopen("path.in","r",stdin),freopen("path.out","w",stdout);
		n=qr,m=qr,q=qr;
		fo(i,1,n) v[i]=qr,dh[i][i].d=0,dh[i][i].maxx=v[i];
		fo(i,1,m)
		{
			int a=qr,b=qr,c=qr;
			if(dh[a][b].d==-1||c<dh[a][b].d)
				dh[a][b].d=dh[b][a].d=c,
				dh[a][b].maxx=dh[b][a].maxx=max(v[a],v[b]);
		}
		fo(k,1,n)
			fo(i,1,n)
				fo(j,1,n)
					if(Wck(i,j,k))
						dh[i][j].d=dh[i][k].d+dh[k][j].d,
						dh[i][j].maxx=max(dh[i][k].maxx,dh[k][j].maxx);
		fo(i,1,q)
		{
			int a=qr,b=qr;
			if(dh[a][b].d==-1) printf("-1\n");
			else printf("%lld\n",dh[a][b].d+dh[a][b].maxx);
		}
		return Ratio;
	}
}
int main(){return Wisadel::main();}
正解
const int Ratio=0;
const int N=305;
int n,m,q;
struct rmm
{
	int v,id;
}c[N];
bool cmp(rmm a,rmm b)
{
	return a.v<b.v;
}
ll dh[N][N],ans[N][N];
namespace Wisadel
{
	short main()
	{
		freopen("path.in","r",stdin),freopen("path.out","w",stdout);
		n=qr,m=qr,q=qr;
		memset(dh,0x3f,sizeof dh);
		memset(ans,0x3f,sizeof ans);
		fo(i,1,n) c[i].v=qr,dh[i][i]=0,c[i].id=i;
		sort(c+1,c+1+n,cmp);
		fo(i,1,m)
		{
			int a=qr,b=qr,c=qr;
			if(c<dh[a][b]) dh[a][b]=dh[b][a]=c;
		}
		fo(k,1,n)
			fo(i,1,n)
				fo(j,1,n)
					dh[c[i].id][c[j].id]=min(dh[c[i].id][c[j].id],dh[c[i].id][c[k].id]+dh[c[k].id][c[j].id]),
					ans[c[i].id][c[j].id]=min(ans[c[i].id][c[j].id],dh[c[i].id][c[j].id]+max(c[i].v,max(c[j].v,c[k].v)));
		fo(i,1,q)
		{
			int a=qr,b=qr;
			if(ans[a][b]>=1e18) printf("-1\n");
			else printf("%lld\n",ans[a][b]);
		}
		return Ratio;
	}
}
int main(){return Wisadel::main();}

B.方格取数

洛谷原题[POI2008] KUP-Plot purchase

赛时写了暴搜,但是假了好像,重评两次只有 \(20pts\)

首先输入时就能判的点,如果某点权值 \(v\) 满足 \(k\le v \le 2\times k\),那么直接可以作为答案输出即可,如果 \(v \ge 2\times k\),那么也显然用不到它。

所以只能选 \(v\le k\) 的点,那么子矩形权值和满足 \(vsum \ge k\),等价于找到答案。

用二维前缀和维护一下,每次求和复杂度为 \(O(1)\)。由于变化是连续的,所以我们按行数不断删去,当存在一行的 \(vsum>2\times k\) 时就按格删去即可。

赛时码太唐了,没意义不放了。

Code:
const int Ratio=0;
const int N=2005;
int n,top;
ll k,sum,d[N][N];
ll qz[N][N];
int up[N],st[N];
namespace Wisadel
{
	ll Ws(int li,int lj,int ri,int rj)
	{
		return qz[ri][rj]-qz[li][rj]-qz[ri][lj]+qz[li][lj];
	}
	short main()
	{
		freopen("matrix.in","r",stdin),freopen("matrix.out","w",stdout);
		n=qr,k=qr;
		fo(i,1,n)
			fo(j,1,n) d[i][j]=qr;
		fo(i,1,n)
			fo(j,1,n) qz[i][j]=qz[i][j-1]+d[i][j];
		fo(i,1,n)
			fo(j,1,n) qz[i][j]+=qz[i-1][j];
		fo(i,1,n)
			fo(j,1,n+1)
			{
				if(d[i][j]<=2*k&&j<=n)
				{
					up[j]++;
					if(d[i][j]>=k)
					{
						printf("%d %d %d %d\n",i,j,i,j);
						return Ratio;
					}
				}
				else up[j]=0;
				while(top&&up[st[top]]>=up[j])
				{
					int lj=st[top-1],rj=j-1;
					int li=i-up[st[top]];
					if(Ws(li,lj,i,rj)>=k)
					{
						while(Ws(li,lj,i,rj)>2*k)
						{
							if(Ws(li,lj,li+1,rj)>2*k)
							{
								while(Ws(li,lj,li+1,rj)>2*k)++lj;
								printf("%d %d %d %d\n",li+1,lj+1,li+1,rj);
								return Ratio;
							}
							li++;
						}
						printf("%d %d %d %d\n",li+1,lj+1,i,rj);
						return Ratio;
					}
					top--;
				}
				if(j<=n) st[++top]=j;
			}
		printf("-1\n");
		return Ratio;
	}
}
int main(){return Wisadel::main();}

C.数组

洛谷原题[CF1114F] Please, another Queries on Array?

看到线段树本来都力了,昨天刚好打了线段树维护区间积的板子,结果遇到了已经忘得差不多了的欧拉函数。

点击撅了欧拉

好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉
好想撅了欧拉

就只记得求单个欧拉函数的板子,然后就唐唐地求完积再求值,然后样例就爆了,然后就把取模删了,然后全 WA 了。

正解如下:

因为欧拉函数不可取模,所以我们直接把它拆开。\(300\) 以内只有 \(62\) 个质数,可以用枚举 + \(bitset\) 记录积所含的质因数,到最后现算就行。

Code:
const int N=4e5+5;
const ll mod=1e9+7;
ll n,q;
ll a[N],c[N];
ll prime[]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293};
namespace Wisadel
{
	struct rmm
	{
		ll w,mark;
		bitset<70>p,markp;
	}t[N<<2],T;
	ll Wqp(ll x,ll y)
	{
		ll res=1;
		while(y)
		{
			if(y&1) res=res*x%mod;
			x=x*x%mod;
			y>>=1;
		}
		return res;
	}
	#define ls (rt<<1)
	#define rs (rt<<1|1)
	#define mid ((l+r)>>1)
	void Wpushup(int rt)
	{
		t[rt].w=t[ls].w*t[rs].w%mod;
		t[rt].p=t[ls].p|t[rs].p;
	}
	void Wpushdown(int rt,int l,int r)
	{
		if(t[rt].mark!=1)
		{
			t[ls].mark=t[ls].mark*t[rt].mark%mod;
			t[rs].mark=t[rs].mark*t[rt].mark%mod;
			t[ls].w=t[ls].w*Wqp(t[rt].mark,mid-l+1)%mod;
			t[rs].w=t[rs].w*Wqp(t[rt].mark,r-mid)%mod;
			t[ls].p|=t[rt].markp;
			t[rs].p|=t[rt].markp;
			t[ls].markp|=t[rt].markp;
			t[rs].markp|=t[rt].markp;
			t[rt].mark=1;
			t[rt].markp.reset();
		}
	}
	void Wbuild(int rt,int l,int r)
	{
		t[rt].mark=1;
		if(l==r)
		{
			t[rt].w=a[l];
			for(ll i=1;i<=62&&prime[i]<=a[l];i++)
				t[rt].p[i]=(a[l]%prime[i]==0);
			return;
		}
		Wbuild(ls,l,mid),Wbuild(rs,mid+1,r);
		Wpushup(rt);
	}
	void Wupd(int rt,int l,int r,int x,int y,int k)
	{
		if(x<=l&&r<=y)
		{
			t[rt].w=t[rt].w*Wqp(k,r-l+1)%mod;
			t[rt].mark=t[rt].mark*k%mod;
			fo(i,1,62)
				t[rt].p[i]=t[rt].p[i]|(k%prime[i]==0),
				t[rt].markp[i]=t[rt].markp[i]|(k%prime[i]==0);
			return;
		}
		Wpushdown(rt,l,r);
		if(x<=mid) Wupd(ls,l,mid,x,y,k);
		if(y>mid) Wupd(rs,mid+1,r,x,y,k);
		Wpushup(rt);
	}
	rmm Wq(int rt,int l,int r,int x,int y)
	{
		if(x<=l&&r<=y) return t[rt];
		Wpushdown(rt,l,r);
		rmm ans,num;
		ans.w=1,ans.p.reset();
		if(x<=mid)
		{
			num=Wq(ls,l,mid,x,y);
			ans.w=ans.w*num.w%mod;
			ans.p|=num.p;
		}
		if(y>mid)
		{
			num=Wq(rs,mid+1,r,x,y);
			ans.w=ans.w*num.w%mod;
			ans.p|=num.p;
		}
		return ans;
	}
	short main()
	{
		freopen("array.in","r",stdin),freopen("array.out","w",stdout);
		n=qr,q=qr;
		fo(i,1,n) a[i]=qr;
		fo(i,1,62) c[i]=(prime[i]-1)*Wqp(prime[i],mod-2)%mod;
		Wbuild(1,1,n);
		fo(i,1,q)
		{
			int op=qr,a=qr,b=qr,cc;
			if(op==1) cc=qr,Wupd(1,1,n,a,b,cc);
			else
			{
				T=Wq(1,1,n,a,b);
				fo(j,1,62)
					if(T.p[j]==1)
						T.w=T.w*c[j]%mod;
				printf("%lld\n",T.w);
			}
		}
		return Ratio;
	}
}
int main(){return Wisadel::main();}

D.树

洛谷原题[POI2015] ODW

看到给部分分给得很详细啊,果断打了四个 namespace,结果把最简单的第二个 Subtask WA 了。

Subtask1:\(n\le 1000\),考虑用 Floyd 找到两点间最短路径长度(也就只会这个了逝罢),然后暴力寻找每次停留点,加和求出答案即可。

Subtask3:(我认为)最简单的一个。链的性质很好啊,在这道题等价于知道了两点的序号就获得了两点的路径长,while 循环不断跳跃即可。

Subtask2&4:利用数据水的特性,直接利用树链剖分和 LCA 暴力就过了,恼,赛时就没想暴力跑,挂 \(55pts\)

Code from namespace狂人
const int N=5e4+5;
int n;
int a[N],b[N],c[N];
int hh[N],to[N<<1],ne[N<<1],dis[N<<1],cnt;
bool subtask2=1,subtask3=1;
namespace Wisadeltask1
{
	int dh[1005][1005];
	short main()
	{
		memset(dh,0x3f,sizeof dh);
		fo(i,1,n) a[i]=qr;
		fo(i,1,n-1)
		{
			int a=qr,b=qr;
			dh[a][b]=dh[b][a]=1;
		}
		fo(i,1,n) b[i]=qr,dh[i][i]=0;
		fo(i,1,n-1) c[i]=qr;
		fo(k,1,n)
			fo(i,1,n)
				fo(j,1,n)
					dh[i][j]=min(dh[i][j],dh[i][k]+dh[k][j]);
		fo(i,1,n-1)
		{
			int u=b[i],v=b[i+1];
			int len=dh[u][v];
			int tim=len/c[i],ans=a[u];
			fo(tt,1,tim)
			{
				fo(j,1,n)
					if(dh[u][j]==c[i]&&dh[u][j]+dh[j][v]==dh[u][v])
					{
						u=j;ans+=a[u];
						break;
					}
			}
			printf("%d\n",ans);
		}
		return Ratio;
	}
}
namespace Wisadeltask2
{
	int dep[N],fx[17][N],zu[N],bigson[N],siz[N];
	void Wdfs(int u,int fa)
	{
		fx[0][u]=fa,dep[u]=dep[fa]+1;
		for(int i=hh[u];i!=-1;i=ne[i])
		{
			int v=to[i];
			if(v==fa) continue;
			Wdfs(v,u);
		}
	}
	int Wlca(int u,int v)
	{
		if(dep[u]<dep[v]) swap(u,v);
		fu(i,16,0)
			if(dep[u]-dep[v]>=(1<<i))
				u=fx[i][u];
		fu(i,16,0)
			if(fx[i][u]!=fx[i][v])
				u=fx[i][u],v=fx[i][v];
		if(u==v) return u;
		else return fx[0][u];
	}
	int Wto(int u,int stp)
	{
		int st=0;
		while(stp)
		{
			if(stp&1) u=fx[st][u];
			stp>>=1;
			st++;
		}
		return u;
	}
	int Wsol(int u,int v,int len)
	{
		int ans=a[u]+a[v],rt=Wlca(u,v);
		while(dep[u]-dep[rt]>=len)
			u=Wto(u,len),ans+=a[u];
		while(dep[v]-dep[rt]>=len)
			v=Wto(v,len),ans+=a[v];
		if(u==v) ans-=a[u];
		return ans;
	}
	short main()
	{
		Wdfs(1,0);
		fo(i,1,16)
			fo(j,1,n)
				fx[i][j]=fx[i-1][fx[i-1][j]];
		fo(i,1,n-1)
			printf("%d\n",Wsol(b[i],b[i+1],c[i]));
		return Ratio;
	}
}
namespace Wisadeltask3
{
	short main()
	{
		fo(i,1,n-1)
		{
			int u,v,ans=0;
			if(b[i+1]<b[i]) u=b[i+1],v=b[i];
			else u=b[i],v=b[i+1];
			ans=a[u];
			while(u!=v) u+=c[i],ans+=a[u];
			printf("%d\n",ans);
		}
		return Ratio;
	}
}
namespace Wisadel
{
	void Wadd(int u,int v)
	{
		to[++cnt]=v;
		dis[cnt]=1;
		ne[cnt]=hh[u];
		hh[u]=cnt;
	}
	short main()
	{
		freopen("tree.in","r",stdin),freopen("tree.out","w",stdout);
		memset(hh,-1,sizeof hh);
		n=qr;
		if(n<=1000) return Wisadeltask1::main();
		fo(i,1,n)
		{
			a[i]=qr;
			if(a[i]!=1) subtask2=0;
		}
		fo(i,1,n-1)
		{
			int u=qr,v=qr;
			if(v!=u+1) subtask3=0;
			Wadd(u,v),Wadd(v,u);
		}
		fo(i,1,n) b[i]=qr;
		fo(i,1,n-1) c[i]=qr;
		if(subtask3) return Wisadeltask3::main();
		return Wisadeltask2::main();
	}
}
int main(){return Wisadel::main();}

完结撒花~

picture from hzoi_Shadow
image

posted @ 2024-07-12 18:51  DrRatio  阅读(40)  评论(2编辑  收藏  举报