CSP提高组模拟1

CSP提高组模拟1

\(T1\) T5420. 最短路 \(80pts\)

  • 原题: luogu P2966 [USACO09DEC] Cow Toll Paths G

  • \(Floyd\) 的本质是动态规划,最外层枚举的 \(k\) 是阶段,表示中转点的编号不超过 \(k\) 。同样地,本题中可以对点权排序,转移过程中钦定 \(k\) 是最大的,而 \(i,j\) 同理。

  • 数据有点水,将边权和点权混在一起转移的错解因写得有点好看拿到了 \(80pts\) 。跑两遍 \(Floyd\)\(AC\) 了。

    点击查看代码
    pair<ll,ll>a[310];
    ll dis[310][310],ans[310][310];
    int main()
    {
    	freopen("path.in","r",stdin);
    	freopen("path.out","w",stdout);
    	ll n,m,q,u,v,w,i,j,k;
    	cin>>n>>m>>q;
    	memset(dis,0x3f,sizeof(dis));
    	memset(ans,0x3f,sizeof(ans));
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i].first;
    		a[i].second=i;
    	}
    	sort(a+1,a+1+n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v>>w;
    		dis[u][v]=dis[v][u]=min(dis[u][v],w);
    	}
    	for(k=1;k<=n;k++)
    	{
    		for(i=1;i<=n;i++)
    		{
    			if(a[i].second!=a[k].second)
    			{
    				for(j=1;j<=n;j++)
    				{
    					if(a[i].second!=a[j].second&&a[k].second!=a[j].second)
    					{
    						dis[a[i].second][a[j].second]=min(dis[a[i].second][a[j].second],dis[a[i].second][a[k].second]+dis[a[k].second][a[j].second]);
    						ans[a[i].second][a[j].second]=min(ans[a[i].second][a[j].second],dis[a[i].second][a[j].second]+max(a[i].first,max(a[j].first,a[k].first)));
    					}
    				}
    			}
    		}
    	}
    	for(i=1;i<=q;i++)
    	{
    		cin>>u>>v;
    		cout<<((ans[u][v]==0x3f3f3f3f3f3f3f3f)?-1:ans[u][v])<<endl;
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

\(T2\) T5421. 方格取数 \(35pts\)

  • 原题 : luogu P3474 [POI2008] KUP-Plot purchase

  • 部分分

    • \(35pts\) :枚举每个子矩形,二维前缀和维护,时间复杂度为 \(O(n^{4})\)
      • 在枚举的过程中如果 \(S<k\)\(S>2k\) 了则及时停止了也需要及时停止。但这两种写法不能同时写,涉及到枚举顺序问题。
      • 据此做法,还有一些玄学优化,但仅能减小常数。
        • 最优性剪枝。
    • \(10pts\)\(S \in [2,4]\) 说明最多有 \(4\) 个格子,从每个位置进行扩展即可。
    • \(5pts\) :找到一个位置的数 \(\le 2k\) 即可。
    • \(50pts\) :发现 \(S\) 具有单调性,可以二分查找其中一个坐标,将时间复杂度降至 \(O(n^{3} \log_{2}n)\)
      • 此做法是假的,在 luogu 上判无解放不过去。

        点击查看代码
        ll a[2010][2010],sum[2010][2010];
        ll query(ll x1,ll y1,ll x2,ll y2)
        {
        	return sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];
        }
        int main()
        {
        	freopen("matrix.in","r",stdin);
        	freopen("matrix.out","w",stdout);
        	ll n,m,i,j,k,h,l,r,mid;
        	scanf("%lld%lld",&n,&m);
        	for(i=1;i<=n;i++)
        	{
        		for(j=1;j<=n;j++)
        		{
        			scanf("%lld",&a[i][j]);
        		}
        	}
        	for(i=1;i<=n;i++)
        	{
        		for(j=1;j<=n;j++)
        		{
        			sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
        		}
        	}
        	for(i=1;i<=n;i++)
        	{
        		for(j=1;j<=n;j++)
        		{
        			for(k=1;k<=i;k++)
        			{
        				l=1;
        				r=j;
        				while(l<=r)
        				{
        					mid=(l+r)/2;
        					if(m<=query(k,mid,i,j)&&query(k,mid,i,j)<=2*m)
        					{
        						printf("%lld %lld %lld %lld\n",k,mid,i,j);
        						goto label;
        					}
        					else
        					{
        						l=mid+1;
        					}
        				}
        			}
        		}
        	}
        	printf("-1\n");
        	label:
        	fclose(stdin);
        	fclose(stdout);
        	return 0;
        }
        
    • \(100pts\) :继续二分查找另一个坐标,时间复杂度为 \(O(n^{2} \log_{2}^{2}n)\) ,加个超级快读卡卡常就 \(AC\) 了。
      • 此做法是假的,在 luogu 上判无解放不过去。

        点击查看代码
        #define LOCAL
        namespace IO{
        	#ifdef LOCAL
        	FILE*Fin(fopen("matrix.in","r")),*Fout(fopen("matrix.out","w"));
        	#else
        	FILE*Fin(stdin),*Fout(stdout);
        	#endif
        	class qistream{static const size_t SIZE=1<<16,BLOCK=64;FILE*fp;char buf[SIZE];int p;public:qistream(FILE*_fp=stdin):fp(_fp),p(0){fread(buf+p,1,SIZE-p,fp);}void flush(){memmove(buf,buf+p,SIZE-p),fread(buf+SIZE-p,1,p,fp),p=0;}qistream&operator>>(char&x){x=getch();while(isspace(x))x=getch();return*this;}template<class T>qistream&operator>>(T&x){x=0;p+BLOCK>=SIZE?flush():void();bool flag=false;for(;!isdigit(buf[p]);++p)flag=buf[p]=='-';for(;isdigit(buf[p]);++p)x=x*10+buf[p]-'0';x=flag?-x:x;return*this;}char getch(){p+BLOCK>=SIZE?flush():void();return buf[p++];}qistream&operator>>(char*str){char ch=getch();while(ch<=' ')ch=getch();int i=0;for(;ch>' ';++i,ch=getch())str[i]=ch;str[i]='\0';return*this;}}qcin(Fin);
        	class qostream{static const size_t SIZE=1<<16,BLOCK=64;FILE*fp;char buf[SIZE];int p;public:qostream(FILE*_fp=stdout):fp(_fp),p(0){}~qostream(){fwrite(buf,1,p,fp);}void flush(){fwrite(buf,1,p,fp),p=0;}template<class T>qostream&operator<<(T x){int len=0;p+BLOCK>=SIZE?flush():void();x<0?(x=-x,buf[p++]='-'):0;do buf[p+len]=x%10+'0',x/=10,++len;while(x);for(int i=0,j=len-1;i<j;++i,--j)std::swap(buf[p+i],buf[p+j]);p+=len;return*this;}qostream&operator<<(char x){putch(x);return*this;}void putch(char ch){p+BLOCK>=SIZE?flush():void();buf[p++]=ch;}qostream&operator<<(char*str){for(int i=0;str[i];++i)putch(str[i]);return*this;}qostream&operator<<(const char*s){for(int i=0;s[i];++i)putch(s[i]);return*this;}}qcout(Fout);
        }
        #define cin IO::qcin
        #define cout IO::qcout
        ll sum[2010][2010];
        inline ll query(ll x1,ll y1,ll x2,ll y2)
        {
        	return sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];
        }
        int main()
        {
        	freopen("matrix.in","r",stdin);
        	freopen("matrix.out","w",stdout);
        	ll n,m,i,j,num,minn,lk,rk,midk,lh,rh,midh;
        	cin>>n>>m;
        	for(i=1;i<=n;i++)
        	{
        		for(j=1;j<=n;j++)
        		{
        			cin>>sum[i][j];
        			sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+sum[i][j];
        		}
        	}
        	for(i=1;i<=n;i++)
        	{
        		for(j=1;j<=n;j++)
        		{
        			lk=1;
        			rk=i;
        			while(lk<=rk)
        			{
        				midk=(lk+rk)>>1;
        				lh=1;
        				rh=j;
        				minn=0x7f7f7f7f7f7f7f7f;
        				while(lh<=rh)
        				{
        					midh=(lh+rh)>>1;
        					num=query(midk,midh,i,j);
        					minn=min(minn,num);
        					if(m<=num&&num<=2*m)
        					{
        						cout<<midk<<" "<<midh<<" "<<i<<" "<<j<<endl;
        						goto label;
        					}
        					else
        					{
        						if(num<m)
        						{
        							rh=midh-1;
        						}
        						else
        						{
        							lh=midh+1;
        						}
        					}
        				}
        				if(minn<m)
        				{
        					rk=midk-1;
        				}
        				else
        				{
        					lk=midk+1;
        				}
        			}
        		}
        	}
        	cout<<"-1"<<endl;
        	label:
        	fclose(stdin);
        	fclose(stdout);
        	return 0;
        }
        
  • 正解

    • \(\exists i,j \in [1,n],k \le a_{i,j} \le 2k\) ,则 \((i,j)\) 即为所求。假设此时矩阵中不再含有 \(k \le a_{i,j} \le 2k\) 的元素。
    • \(a_{i,j}>2k\) 的视作 \(0\) ,将 \(a_{i,j}<k\) 的视作 \(1\) ,那么最终选出的矩阵一定全是 \(1\)
    • 单调栈维护出最大子矩形,答案矩阵一定被包含在其中。
    • 一行一行切,如果切下来的能通过一列一列切列直到符合题意能输出,否则舍弃这一行。
    点击查看代码
    ll a[2010][2010],sum[2010][2010],f[2010][2010],l[2010],r[2010];
    stack<ll>s;
    ll query(ll x1,ll y1,ll x2,ll y2)
    {
    	return sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];
    }
    int main()
    {   
    	freopen("matrix.in","r",stdin);
    	freopen("matrix.out","w",stdout);
    	ll n,k,flag=0,ans=0,x1=0,y1=0,x2=0,y2=0,i,j;
    	scanf("%lld%lld",&n,&k);
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=n;j++)
    		{
    			scanf("%lld",&a[i][j]);
    			if(k<=a[i][j]&&a[i][j]<=2*k)
    			{ 
    				printf("%lld %lld %lld %lld\n",i,j,i,j);
    				flag=1;
    			}
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=n;j++)
    		{
    			sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
    		}
    	}
    	if(flag==0)
    	{
    		for(i=1;i<=n;i++)
    		{
    			while(s.empty()==0)
    			{
    				s.pop();
    			}
    			f[i][0]=-1;
    			s.push(0);
    			for(j=1;j<=n;j++)
    			{
    				f[i][j]=(a[i][j]<k)*(f[i-1][j]+1);
    				while(s.empty()==0&&f[i][s.top()]>=f[i][j])
    				{
    					s.pop();
    				}
    				l[j]=(s.empty()==0)?s.top()+1:1;
    				s.push(j);
    			}
    			while(s.empty()==0)
    			{
    				s.pop();
    			}
    			f[i][n+1]=-1;
    			s.push(n+1);
    			for(j=n;j>=1;j--)
    			{
    				while(s.empty()==0&&f[i][s.top()]>=f[i][j])
    				{
    					s.pop();
    				}
    				r[j]=(s.empty()==0)?s.top()-1:-1;
    				s.push(j);  
    				if(f[i][j]!=0)
    				{
    					if(k<=query(i-f[i][j]+1,l[j],i,r[j])&&query(i-f[i][j]+1,l[j],i,r[j])<=2*k)
    					{
    						printf("%lld %lld %lld %lld\n",i-f[i][j]+1,l[j],i,r[j]);
    						flag=1;
    						break;
    					}
    					else
    					{
    						if(query(i-f[i][j]+1,l[j],i,r[j])>ans)
    						{
    							ans=query(i-f[i][j]+1,l[j],i,r[j]);
    							x1=i-f[i][j]+1;
    							y1=l[j];
    							x2=i;
    							y2=r[j];
    						}
    					}
    				}
    			}
    			if(flag==1)
    			{
    				break;
    			}
    		}
    		if(flag==0)
    		{
    			if(ans<k)
    			{
    				printf("-1\n");
    			}
    			else
    			{
    				for(i=x2;i>=x1;i--)
    				{
    					if(k<=query(i,y1,i,y2)&&query(i,y1,i,y2)<=2*k)
    					{
    						printf("%lld %lld %lld %lld\n",i,y1,i,y2);
    						break;
    					}
    					else
    					{
    						if(query(i,y1,i,y2)<k)
    						{
    							ans-=query(i,y1,i,y2);
    							if(k<=ans&&ans<=2*k)
    							{
    								printf("%lld %lld %lld %lld\n",x1,y1,i-1,y2);
    								break;
    							}
    						}
    						else
    						{
    							ans=query(i,y1,i,y2);
    							for(j=y2;j>=y1;j--)
    							{
    								ans-=a[i][j];
    								if(k<=ans&&ans<=2*k)
    								{
    									printf("%lld %lld %lld %lld\n",i,y1,i,j-1);
    									flag=1;
    									break;
    								}
    							}
    							if(flag==1)
    							{
    								break;
    							}
    						}
    					}
    				}
    			}
    		}
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

\(T3\) T5422. 数组 \(100pts\)

  • 原题: CF1114F Please, another Queries on Array?

  • 正解

    • 如果没有修改操作的话就是 BZOJ4026 dC Loves Number Theory 了,但本题的做法无法硬套进那题,因为与值域有关。
    • 有欧拉函数计算式为 \(\varphi(n)=n \prod\limits_{p|n,p \in \mathbb{P}} \frac{p-1}{p}\)
    • 观察到 \(a_{i},x \le 300\) ,那么最后的乘积所包含的质因子很少(最多有 \(62\) 个)。线段树维护最后的乘积及所包含的质因子即可,前后者都可以通过打懒标记来做。
    • 略带卡常。
    点击查看代码
    const ll p=1000000007;
    ll a[100010],prime[70]={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},c[70];
    ll qpow(ll a,ll b,ll p)
    {
    	ll ans=1;
    	while(b)
    	{
    		if(b&1)
    		{
    			ans=ans*a%p;
    		}
    		b>>=1;
    		a=a*a%p;
    	}
    	return ans;
    }
    struct SMT
    {
    	struct SegmentTree
    	{
    		ll l,r,mul,lazy;
    		bitset<70>vis_mul,vis_lazy;
    	}tree[400010],ls;
    	ll lson(ll x)
    	{
    		return x*2;
    	}
    	ll rson(ll x)
    	{
    		return x*2+1;
    	}
    	void pushup(ll rt)
    	{
    		tree[rt].mul=tree[lson(rt)].mul*tree[rson(rt)].mul%p;
    		tree[rt].vis_mul=tree[lson(rt)].vis_mul|tree[rson(rt)].vis_mul;
    	}
    	void build(ll rt,ll l,ll r)
    	{
    		tree[rt].l=l;
    		tree[rt].r=r;
    		tree[rt].lazy=1;
    		if(l==r)
    		{
    			tree[rt].mul=a[l];
    			for(ll i=1;i<=62&&prime[i]<=a[l];i++)
    			{
    				tree[rt].vis_mul[i]=(a[l]%prime[i]==0);
    			}
    			return;
    		}
    		ll mid=(tree[rt].l+tree[rt].r)/2;
    		build(lson(rt),l,mid);
    		build(rson(rt),mid+1,r);
    		pushup(rt);
    	}
    	void pushdown(ll rt)
    	{
    		if(tree[rt].lazy!=1)
    		{
    			tree[lson(rt)].lazy=tree[lson(rt)].lazy*tree[rt].lazy%p;
    			tree[lson(rt)].vis_lazy|=tree[rt].vis_lazy;
    			tree[lson(rt)].vis_mul|=tree[rt].vis_lazy;
    			tree[lson(rt)].mul=(tree[lson(rt)].mul*qpow(tree[rt].lazy,tree[lson(rt)].r-tree[lson(rt)].l+1,p))%p;
    			tree[rson(rt)].lazy=tree[rson(rt)].lazy*tree[rt].lazy%p;
    			tree[rson(rt)].vis_lazy|=tree[rt].vis_lazy;
    			tree[rson(rt)].vis_mul|=tree[rt].vis_lazy;
    			tree[rson(rt)].mul=(tree[rson(rt)].mul*qpow(tree[rt].lazy,tree[rson(rt)].r-tree[rson(rt)].l+1,p))%p;
    			tree[rt].lazy=1;
    			tree[rt].vis_lazy.reset();
    		}
    	}
    	void update(ll rt,ll x,ll y,ll val)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)
    		{
    			tree[rt].lazy=tree[rt].lazy*val%p;
    			for(ll i=1;i<=62&&prime[i]<=val;i++)
    			{
    				tree[rt].vis_mul[i]=tree[rt].vis_mul[i]|(val%prime[i]==0);
    				tree[rt].vis_lazy[i]=tree[rt].vis_lazy[i]|(val%prime[i]==0);
    			}
    			tree[rt].mul=(tree[rt].mul*qpow(val,tree[rt].r-tree[rt].l+1,p))%p;
    			return;
    		}
    		pushdown(rt);
    		ll mid=(tree[rt].l+tree[rt].r)/2;
    		if(x<=mid)
    		{
    			update(lson(rt),x,y,val);
    		}
    		if(y>mid)
    		{
    			update(rson(rt),x,y,val);
    		}
    		pushup(rt);
    	}
    	SegmentTree query(ll rt,ll x,ll y)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)
    		{
    			return tree[rt];
    		}
    		pushdown(rt);
    		ll mid=(tree[rt].l+tree[rt].r)/2;
    		SegmentTree ans,num;
    		ans.mul=1;
    		ans.vis_mul.reset();
    		if(x<=mid)
    		{
    			num=query(lson(rt),x,y);
    			ans.mul=ans.mul*num.mul%p;
    			ans.vis_mul|=num.vis_mul;
    		}
    		if(y>mid)
    		{
    			num=query(rson(rt),x,y);
    			ans.mul=ans.mul*num.mul%p;
    			ans.vis_mul|=num.vis_mul;
    		}
    		return ans;
    	}
    }T;
    int main()
    {
    	freopen("array.in","r",stdin);
    	freopen("array.out","w",stdout);
    	ll n,q,pd,l,r,x,i,j;
    	scanf("%lld%lld",&n,&q);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%lld",&a[i]);
    	}
    	for(i=1;i<=62;i++)
    	{
    		c[i]=(prime[i]-1)*qpow(prime[i],p-2,p)%p;
    	}
    	T.build(1,1,n);
    	for(i=1;i<=q;i++)
    	{
    		scanf("%lld%lld%lld",&pd,&l,&r);
    		if(pd==1)
    		{
    			scanf("%lld",&x);
    			T.update(1,l,r,x);
    		}
    		else
    		{
    			T.ls=T.query(1,l,r);
    			for(j=1;j<=62;j++)
    			{
    				if(T.ls.vis_mul[j]==1)
    				{
    					T.ls.mul=(T.ls.mul*c[j])%p;
    				}
    			}
    			printf("%lld\n",T.ls.mul);
    		}
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

\(T4\) T5423. 树 \(0pts\)

  • 原题: luogu P3591 [POI2015] ODW

  • 考虑根号分治。

  • 当步长大于 \(\sqrt{n}\) 时步数小于 \(\sqrt{n}\) 可以暴力跳;否则对于每个步长预处理到根节点的贡献,接着类似求树上两点距离差分维护即可。跳步长时需要求 \(k\) 级祖先,详见 2024初三年前集训测试2 T4 金牌

  • 特判跳到 \(LCA\) 时多产生贡献。

  • 略带卡常。

    点击查看代码
    #define LOCAL
    namespace IO{
    	#ifdef LOCAL
    	FILE*Fin(fopen("tree.in","r")),*Fout(fopen("tree.out","w"));
    	#else
    	FILE*Fin(stdin),*Fout(stdout);
    	#endif
    	class qistream{static const size_t SIZE=1<<16,BLOCK=64;FILE*fp;char buf[SIZE];int p;public:qistream(FILE*_fp=stdin):fp(_fp),p(0){fread(buf+p,1,SIZE-p,fp);}void flush(){memmove(buf,buf+p,SIZE-p),fread(buf+SIZE-p,1,p,fp),p=0;}qistream&operator>>(char&x){x=getch();while(isspace(x))x=getch();return*this;}template<class T>qistream&operator>>(T&x){x=0;p+BLOCK>=SIZE?flush():void();bool flag=false;for(;!isdigit(buf[p]);++p)flag=buf[p]=='-';for(;isdigit(buf[p]);++p)x=x*10+buf[p]-'0';x=flag?-x:x;return*this;}char getch(){p+BLOCK>=SIZE?flush():void();return buf[p++];}qistream&operator>>(char*str){char ch=getch();while(ch<=' ')ch=getch();int i=0;for(;ch>' ';++i,ch=getch())str[i]=ch;str[i]='\0';return*this;}}qcin(Fin);
    	class qostream{static const size_t SIZE=1<<16,BLOCK=64;FILE*fp;char buf[SIZE];int p;public:qostream(FILE*_fp=stdout):fp(_fp),p(0){}~qostream(){fwrite(buf,1,p,fp);}void flush(){fwrite(buf,1,p,fp),p=0;}template<class T>qostream&operator<<(T x){int len=0;p+BLOCK>=SIZE?flush():void();x<0?(x=-x,buf[p++]='-'):0;do buf[p+len]=x%10+'0',x/=10,++len;while(x);for(int i=0,j=len-1;i<j;++i,--j)std::swap(buf[p+i],buf[p+j]);p+=len;return*this;}qostream&operator<<(char x){putch(x);return*this;}void putch(char ch){p+BLOCK>=SIZE?flush():void();buf[p++]=ch;}qostream&operator<<(char*str){for(int i=0;str[i];++i)putch(str[i]);return*this;}qostream&operator<<(const char*s){for(int i=0;s[i];++i)putch(s[i]);return*this;}}qcout(Fout);
    }
    #define cin IO::qcin
    #define cout IO::qcout
    struct node
    {
    	int nxt,to;
    }e[100010];
    int head[100010],siz[100010],fa[100010][20],dep[100010],son[100010],top[100010],a[100010],b[100010],c[100010],dis[100010][330],cnt=0;
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    void dfs1(int x,int father)
    {
    	siz[x]=1;
    	fa[x][0]=father;
    	dep[x]=dep[father]+1;
    	for(int i=1;(1<<i)<=dep[x];i++)
    	{
    		fa[x][i]=fa[fa[x][i-1]][i-1];
    	}
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=father)
    		{
    			dfs1(e[i].to,x);
    			siz[x]+=siz[e[i].to];
    			son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x];
    		}
    	}
    }
    void dfs2(int x,int father,int id)
    {
    	top[x]=id;
    	if(son[x]!=0)
    	{
    		dfs2(son[x],x,id);
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(e[i].to!=father&&e[i].to!=son[x])
    			{
    				dfs2(e[i].to,x,e[i].to);
    			}
    		}
    	}
    }
    int lca(int u,int v)
    {
    	while(top[u]!=top[v])
    	{
    		if(dep[top[u]]>dep[top[v]])
    		{
    			u=fa[top[u]][0];
    		}
    		else
    		{
    			v=fa[top[v]][0];
    		}
    	}
    	return (dep[u]<dep[v])?u:v;
    }
    int kth_fa(int x,int k)
    {
    	int rt=x;
    	for(int i=(int)log2(k);i>=0;i--)
    	{
    		if(dep[x]-dep[fa[rt][i]]<=k)
    		{
    			rt=fa[rt][i];
    		}
    	}
    	return rt;
    }
    void dfs3(int x,int father,int c)
    {
    	dis[x][c]=(dep[x]<=c)?a[x]:a[x]+dis[kth_fa(x,c)][c];
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=father)
    		{
    			dfs3(e[i].to,x,c);
    		}
    	}
    }
    int main()
    {
    	int n,s,u,v,rt,ans,r1,r2,i;
    	cin>>n;
    	s=sqrt(n)+1;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	for(i=1;i<=n-1;i++)
    	{
    		cin>>u>>v;
    		add(u,v);
    		add(v,u);
    	}
    	dfs1(1,0);
    	dfs2(1,0,1);
    	for(i=1;i<=n;i++)
    	{
    		cin>>b[i];
    	}
    	for(i=1;i<=n-1;i++)
    	{
    		cin>>c[i];
    		if(c[i]<=s)
    		{
    			if(dis[1][c[i]]==0)
    			{
    				dfs3(1,0,c[i]);
    			}
    		}
    	}
    	for(i=1;i<=n-1;i++)
    	{
    		u=b[i];
    		v=b[i+1];
    		rt=lca(u,v);
    		ans=0;
    		if(c[i]<=s)
    		{   
    			r1=(dep[u]-dep[rt])%c[i];
    			r2=(dep[v]-dep[rt])%c[i];
    			ans=dis[u][c[i]]+dis[v][c[i]]+((r1==0)?-2*dis[rt][c[i]]+a[rt]:-dis[kth_fa(rt,c[i]-r1)][c[i]]-dis[kth_fa(rt,c[i]-r2)][c[i]]); 
    		}
    		else
    		{
    			while(dep[u]>=dep[rt])
    			{
    				ans+=a[u];
    				u=kth_fa(u,c[i]);
    			}
    			while(dep[v]>dep[rt])
    			{
    				ans+=a[v];
    				v=kth_fa(v,c[i]);
    			}
    		}
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    

总结

  • 题面是 \(PDF\) 的形式,用插件能在 \(vscode\) 里打开。
  • \(T1\) 第一眼觉得是 \(Kruskal\) 重构树,然后发现还有点权的限制。
  • \(T2\) 口胡了一个二分套二分的做法,和暴力拍了几百组手动检验正确遂直接交了,赛后发现写假了,挂了 \(15pts\)
  • \(T3\) 写线段树 pushdown 的时候传区间长度传成父亲的区间长度了,幸好小样例有区间修改需要 pushdown 的测试点,马上就改出来了。
  • \(T4\) 读假题了,以为只能走一步,走不到就停下了。

后记

  • 昨晚上就把题面和样例发下来了(下发文件在比赛开始前就可以下载)。

  • \(PDF\) 上比赛编号是 ZZ Round 1,原 \(4.5h\) 的比赛到我们这就成 \(4h\) 了。

  • 赛时关网了,可能是因为还有新高二的跟着打?

  • 组题人的提示比较典。

  • \(T2\) 赛时没有 Special Judge , 赛后的第一份 Special Judge 还出锅了,下午才正式重测。

posted @ 2024-07-12 16:23  hzoi_Shadow  阅读(53)  评论(2编辑  收藏  举报
扩大
缩小