多校A层冲刺NOIP2024模拟赛07

多校A层冲刺NOIP2024模拟赛07

\(T1\) A. 限速(speed) \(40pts\)

  • 设最终保留的边的权值构成的集合为 \(S\) 。那么其贡献为 \(\begin{cases} k-\max\limits_{x \in S}\{ x \} & \max\limits_{x \in S}\{ x \} \le k \\ \sum\limits_{x \in S}[x>k] \times (x-k) & \max\limits_{x \in S}\{ x \}>k \end{cases}\)

  • 两种情况一起考虑贡献不太好做,考虑分开统计。

  • 若仅用边权 \(\le k\) 的边无法使整张图连通,则类似 \(Kruskal\) 的写法逐个将边权 \(>k\) 的边加入其中,直接统计贡献。

  • 若仅用边权 \(\le k\) 的边可以使整张图连通,又分为两种情况。

    • 若仅用边权 \(\le k\) 的边,则直接统计贡献。
    • 否则可以证明最多加入一条边权 \(>k\) 的边(且任意一条边均可以)替换一条边权 \(\le k\) 的边从而使得图连通,得到的贡献和上个贡献取 \(\min\)
    点击查看代码
    struct node
    {
    	ll from,to,w;
    }e[200010];
    bool cmp(node a,node b)
    {
    	return a.w<b.w;
    }
    struct DSU
    {
    	ll fa[200010];
    	void init(ll n)
    	{
    		for(ll i=1;i<=n;i++)
    		{
    			fa[i]=i;
    		}
    	}
    	ll find(ll x)
    	{
    		return (fa[x]==x)?x:fa[x]=find(fa[x]);
    	}
    }D;
    int main()
    {
    	freopen("speed.in","r",stdin);
    	freopen("speed.out","w",stdout);
    	ll n,m,k,len,maxx=0,sum=0,cnt=0,ans=0,x,y,i;
    	cin>>n>>m>>k;
    	len=m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>e[i].from>>e[i].to>>e[i].w;
    	}
    	sort(e+1,e+1+m,cmp);
    	for(i=1;i<=m;i++)
    	{
    		if(e[i].w>k)
    		{
    			len=i-1;
    			break;
    		}
    	}
    	D.init(n);
    	for(i=len;i>=1;i--)
    	{
    		x=D.find(e[i].from);
    		y=D.find(e[i].to);
    		if(x!=y)
    		{
    			D.fa[x]=y;
    			cnt++;
    			maxx=max(maxx,e[i].w);
    		}
    	}
    	if(cnt==n-1)
    	{
    		ans=k-maxx;
    		if(len+1<=m)
    		{
    			ans=min(ans,e[len+1].w-k);
    		}
    	}
    	else
    	{
    		for(i=len+1;i<=m;i++)
    		{
    			x=D.find(e[i].from);
    			y=D.find(e[i].to);
    			if(x!=y)
    			{
    				D.fa[x]=y;
    				sum+=e[i].w-k;
    			}
    		}
    		ans=sum;
    	}
    	cout<<ans<<endl;
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

\(T2\) B. 酒鬼 (drunkard) \(18pts\)

  • 部分分

    • 子任务 \(1\) :先走到 \(q_{i}\) ,然后开始左右移动从而拖延时间,故 \((q_{i}-(p_{i}-1)) \mod 2\) 即为所求。
    • 子任务 \(2\) :直接走到 \(q_{i}\) ,故 \(q_{i}-(p_{i}-1)\) 即为所求。
    点击查看代码
    string s;
    int main()
    {
    	freopen("drunkard.in","r",stdin);
    	freopen("drunkard.out","w",stdout);
    	int n,m,p,q,i;
    	cin>>n>>m;
    	if(m==2)
    	{
    		cin>>s>>p>>q;
    		cin>>s;
    		if(s=="min")
    		{
    			if((q-(p-1))%2==0)
    			{
    				cout<<0<<endl;
    			}
    			else
    			{
    				cout<<1<<endl;
    			}
    		}
    		else
    		{
    			cout<<q-(p-1)<<endl;
    		}
    	}
    	else
    	{
    		for(i=1;i<=m;i++)
    		{
    			cin>>s;
    			if(s=="clue")
    			{
    				cin>>p>>q;
    			}
    			else
    			{
    				cout<<"bad"<<endl;
    			}
    		}
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 正解

    • 考虑将操作按照时间维 \(q\) 升序排序。
    • 一些乱七八糟的性质
      • 发现当 \(t_{0}\) 确定时,每个时刻所在的位置的奇偶性是固定的。
      • 最大值为 \(\infty\) 当且仅当没有操作或 \(\forall i,p_{i}=1\) ,否则一定等于 \(\min\limits_{p_{i} \ge2}\{ q_{i}-(p_{i}-1) \}\)
      • 因为在之前的操作中已经判断了是否有解,故可以直接钦定前后合法,通过时间差和位移差来判断是否合法。
      • \(\forall i,p_{i} \le 2\) 则最小值答案只会是 \(0\)\(1\)\(\exists i,p_{i}=1\) 的情况比较难处理,因为要判断是先走然后再走回来还是一直睡觉在恰当时间开始走,而且不保证奇偶性合法。
  • 考虑特殊处理 \(p_{i}=1\) 的情况。

    • 保留最早的一个 \(p_{i} \ge 2\) 的时间 \(tim\)
    • 当奇偶性不同时更新在符合当前约束下的最晚移动时间 \(last\) ;及时清楚不符合当前约束的 \(last\)
      • \(last>tim\) 则不合法。
      • 询问 \(\min\) 时和上一个时间的出发时间取 \(\min\) 即可。
    点击查看代码
    set<pair<int,int> >s;
    set<pair<int,int> >::iterator it,pre,nxt;
    int main()
    {
    	freopen("drunkard.in","r",stdin);
    	freopen("drunkard.out","w",stdout);
    	int n,m,p,q,maxx=0x7f7f7f7f,tim=0x7f7f7f7f,flag=0,last=-1,i;
    	string pd;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd;
    		if(flag==1)
    		{
    			if(pd=="clue")
    			{
    				cin>>p>>q;
    			}
    			else
    			{
    				cout<<"bad"<<endl;
    			}
    		}
    		else
    		{
    			if(pd=="clue")
    			{
    				cin>>p>>q;
    				if(p-1>q)
    				{
    					flag=1;
    				}
    				else
    				{
    					if(p>=2)
    					{
    						tim=min(tim,q);
    						maxx=min(maxx,q-(p-1));
    					}	
    					it=s.insert(make_pair(q,p)).first;
    					nxt=next(it);
    					if(it!=s.begin())
    					{
    						pre=prev(it);
    						flag|=(abs(pre->second-p)>q-pre->first);
    						last=(nxt!=s.end()&&nxt->first==last)?-1:last;
    						if((pre->first-(pre->second-1))%2!=(q-(p-1))%2)
    						{
    							last=max(last,q);
    						}
    					}
    					if(nxt!=s.end())
    					{	
    						flag|=(abs(nxt->second-p)>nxt->first-q);
    						if((nxt->first-(nxt->second-1))%2!=(q-(p-1))%2)
    						{
    							last=max(last,nxt->first);
    						}
    					}
    					flag|=(last>tim);
    				}
    			}
    			if(pd=="min")
    			{
    				if(last==-1)
    				{
    					cout<<(s.size()==0?0:(s.begin()->first-(s.begin()->second-1))%2)<<endl;
    				}
    				else
    				{
    					it=s.lower_bound(make_pair(last,0));
    					pre=prev(it);
    					cout<<min(pre->first+1,last)<<endl;
    				}
    			}
    			if(pd=="max")
    			{
    				if(maxx==0x7f7f7f7f)
    				{
    					cout<<"inf"<<endl;
    				}
    				else
    				{
    					cout<<maxx<<endl;
    				}
    			}	
    		}
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

\(T3\) C. 距离(distance) \(70pts\)

  • 部分分
    • \(40pts\) :模拟,会被链卡到飞起,时间复杂度为 \(O(n^{3})\)

      点击查看代码
      const int p=1000000007;
      struct node
      {
      	int nxt,to;
      }e[1000010];
      int head[1000010],dfn[1000010],out[1000010],pos[1000010],a[1000010],b[1000010],cnt=0,tot=0;
      void add(int u,int v)
      {
      	cnt++;
      	e[cnt].nxt=head[u];
      	e[cnt].to=v;
      	head[u]=cnt;
      }
      void dfs(int x,int fa)
      {
      	tot++;
      	dfn[x]=tot;
      	pos[tot]=x;
      	for(int i=head[x];i!=0;i=e[i].nxt)
      	{
      		if(e[i].to!=fa)
      		{
      			dfs(e[i].to,x);
      		}
      	}
      	out[x]=tot;
      }
      int qadd(int a,int b,int p)
      {
      	return a+b>=p?a+b-p:a+b;
      }
      int main()
      {
      	freopen("distance.in","r",stdin);
      	freopen("distance.out","w",stdout);
      	int n,u,v,ans=0,i,j,k;
      	scanf("%d",&n);
      	for(i=1;i<=n-1;i++)
      	{
      		scanf("%d%d",&u,&v);
      		add(u,v);
      		add(v,u);
      	}
      	for(i=1;i<=n;i++)
      	{
      		scanf("%d%d",&a[i],&b[i]);
      	}
      	dfs(1,0);
      	for(i=1;i<=n;i++)
      	{
      		ans=0;
      		for(j=dfn[i];j<=out[i];j++)
      		{
      			for(k=dfn[i];k<=out[i];k++)
      			{
      				ans=qadd(ans,min(abs(a[pos[j]]-a[pos[k]]),abs(b[pos[j]]-b[pos[k]])),p);
      			}
      		}
      		printf("%d\n",ans);
      	}
      	fclose(stdin);
      	fclose(stdout);
      	return 0;
      }
      
    • \(70pts\) :子树信息可以直接合并,每对点对之间的贡献只会被算一次,随便写个什么维护即可,时间复杂度为 \(O(n^{2})\)

      点击查看代码
      const int p=1000000007;
      struct node
      {
      	int nxt,to;
      }e[1000010];
      int head[500010],dfn[500010],out[500010],pos[500010],a[500010],b[500010],ans[500010],cnt=0,tot=0;
      vector<short>rt[10010];
      void add(int u,int v)
      {
      	cnt++;
      	e[cnt].nxt=head[u];
      	e[cnt].to=v;
      	head[u]=cnt;
      }
      void merge(int x,int y)
      {
      	if(rt[x].size()<rt[y].size())
      	{
      		swap(rt[x],rt[y]);
      	}
      	for(int i=0;i<rt[y].size();i++)
      	{
      		rt[x].push_back(rt[y][i]);
      	}
      }
      int qadd(int a,int b,int p)
      {
      	return a+b>=p?a+b-p:a+b;
      }
      void dfs(int x,int fa)
      {
      	tot++;
      	dfn[x]=tot;
      	pos[tot]=x;
      	rt[x].push_back(x);
      	for(int i=head[x];i!=0;i=e[i].nxt)
      	{
      		if(e[i].to!=fa)
      		{
      			dfs(e[i].to,x);
      			ans[x]=qadd(ans[x],ans[e[i].to],p);
      			for(int j=0;j<rt[e[i].to].size();j++)
      			{
      				for(int k=0;k<rt[x].size();k++)
      				{
      					ans[x]=qadd(ans[x],min(abs(a[rt[e[i].to][j]]-a[rt[x][k]]),abs(b[rt[e[i].to][j]]-b[rt[x][k]]))*2%p,p);
      				}
      			}
      			merge(x,e[i].to);
      		}
      	}
      	out[x]=tot;
      }
      int main()
      {
      	freopen("distance.in","r",stdin);
      	freopen("distance.out","w",stdout);
      	int n,u,v,i;
      	scanf("%d",&n);
      	for(i=1;i<=n-1;i++)
      	{
      		scanf("%d%d",&u,&v);
      		add(u,v);
      		add(v,u);
      	}
      	for(i=1;i<=n;i++)
      	{
      		scanf("%d%d",&a[i],&b[i]);
      	}
      	dfs(1,0);
      	for(i=1;i<=n;i++)
      	{
      		printf("%d\n",ans[i]);
      	}
      	fclose(stdin);
      	fclose(stdout);
      	return 0;
      }
      
    • \(80pts\)

      • 先将 \(\min(|a_{i}-a_{j}|,|b_{i}-b_{j}|)\) 容斥成 \(|a_{i}-a_{j}|+|b_{i}-b_{j}|-\max(|a_{i}-a_{j}|,|b_{i}-b_{j}|)\) ,难点在于怎么求后面的 \(\max(|a_{i}-a_{j}|,|b_{i}-b_{j}|)\)
      • \(\{ a \}\) 看做横坐标, \(\{ b \}\) 看做纵坐标,等价于询问曼哈顿坐标系下以 \(x\) 为根的子树内的节点两两距离之和。
      • 考虑转化成曼哈顿距离,令 \(\forall i \in [i,n],p_{i}=\frac{a_{i}+b_{i}}{2},q_{i}=\frac{a_{i}-b_{i}}{2}\) ,则 \(\max\limits(|a_{i}-a_{j}|,|b_{i}-b_{j}|)=|p_{i}-p_{j}|+|q_{i}-q_{j}|\) ,等价于询问 \(\sum\limits_{i \in Subtree(x)}\sum\limits_{j \in Subtree(x)}|a_{i}-a_{j}|+|b_{i}-b_{j}|-|p_{i}-p_{j}|-|q_{i}-q_{j}|\)
        • 拆绝对值,有 \(\begin{aligned} &|p_{i}-p_{j}|+|q_{i}-q_{j}| \\ &=\max\limits(p_{i}-p_{j},p_{j}-p_{i})+\max\limits(q_{i}-q_{j},q_{j}-q_{i}) \\ &=\max(\frac{a_{i}+b_{i}-a_{j}-b_{j}}{2},\frac{a_{j}+b_{j}-a_{i}-b_{i}}{2})+\max(\frac{a_{i}-b_{i}-a_{j}+b_{j}}{2},\frac{a_{j}-b_{j}-a_{i}+b_{i}}{2}) \\ &=\max(a_{i}-a_{j},a_{j}-a_{i},b_{i}-b_{j},b_{j}-b_{i}) \\ &=\max(|a_{i}-a_{j}|,|b_{i}-b_{j}|) \end{aligned}\)
      • \(\{ a \}\) 为例,用线段树对 \(\{ a \}\) 开一个桶数组。当插入一个新的数 \(a_{i}\) 时维护 \(<a_{i}, \ge a_{i}\) 的元素的数量及和,从而得到贡献。树上启发式合并的时间复杂度为 \(O(n \log^{2} n)\)
  • 正解
    • 而插入一个值时可能会与多个数产生贡献,考虑线段树合并。

    • 具体地,对于以 \(x\) 为根的子树内的节点的值构成的有序数组 \(\{ a \}\) ,设 \(ans_{l,r},sum_{l,r},cnt_{l,r}\) 分别表示 \([l,r]\) 的贡献/元素和/元素数量,则有 \(ans_{l,r}=ans_{l,mid}+ans_{mid+1,r}+sum_{mid+1,r} \times cnt_{1,mid}-sum_{1,mid} \times cnt_{mid+1,n}\) 。向上递归时 pushup 即可。

    • 时空复杂度为 \(O(n \log n)\)

      点击查看代码
      const ll mod=1000000007;
      struct node
      {
      	int nxt,to;
      }e[1000010];
      int head[500010],cnt=0;
      ll a[500010],aa[500010],b[500010],bb[500010],p[500010],pp[500010],q[500010],qq[500010],ans[500010];
      void add(int u,int v)
      {
      	cnt++;
      	e[cnt].nxt=head[u];
      	e[cnt].to=v;
      	head[u]=cnt;
      }
      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
      {
      	int root[500010],rt_sum=0;
      	struct SegmentTree
      	{
      		int ls,rs;
      		ll sum,cnt,ans;
      	}tree[500010*30];
      	#define lson(rt) (tree[rt].ls)
      	#define rson(rt) (tree[rt].rs)
      	void clear()
      	{
      		rt_sum=0;
      		memset(root,0,sizeof(root));
      	}
      	int build()
      	{
      		rt_sum++;
      		lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=tree[rt_sum].cnt=tree[rt_sum].ans=0;
      		return rt_sum;
      	}
      	void pushup(int rt)
      	{
      		tree[rt].sum=(tree[lson(rt)].sum+tree[rson(rt)].sum)%mod;
      		tree[rt].cnt=(tree[lson(rt)].cnt+tree[rson(rt)].cnt)%mod;
      		tree[rt].ans=((tree[lson(rt)].ans+tree[rson(rt)].ans)%mod+tree[rson(rt)].sum*tree[lson(rt)].cnt%mod-tree[lson(rt)].sum*tree[rson(rt)].cnt%mod+mod)%mod;
      	}
      	void update(int &rt,int l,int r,int pos,int val)
      	{
      		rt=(rt==0)?build():rt;
      		if(l==r)
      		{
      			tree[rt].cnt=(tree[rt].cnt+1)%mod;
      			tree[rt].sum=(tree[rt].sum+val)%mod;
      			return;
      		}
      		int mid=(l+r)/2;
      		if(pos<=mid)
      		{
      			update(lson(rt),l,mid,pos,val);
      		}
      		else
      		{
      			update(rson(rt),mid+1,r,pos,val);
      		}
      		pushup(rt);
      	}
      	int merge(int rt1,int rt2,int l,int r)
      	{
      		if(rt1==0||rt2==0)
      		{
      			return rt1+rt2;
      		}
      		if(l==r)
      		{
      			tree[rt1].sum=(tree[rt1].sum+tree[rt2].sum)%mod;
      			tree[rt1].cnt=(tree[rt1].cnt+tree[rt2].cnt)%mod;
      			return rt1;
      		}
      		int mid=(l+r)/2;
      		lson(rt1)=merge(lson(rt1),lson(rt2),l,mid);
      		rson(rt1)=merge(rson(rt1),rson(rt2),mid+1,r);
      		pushup(rt1);
      		return rt1;
      	}
      }T;
      void dfs(int x,int fa,ll d,ll a[],ll aa[])
      {
      	for(int i=head[x];i!=0;i=e[i].nxt)
      	{
      		if(e[i].to!=fa)
      		{
      			dfs(e[i].to,x,d,a,aa);
      			T.root[x]=T.merge(T.root[x],T.root[e[i].to],1,aa[0]);
      		}
      	}
      	T.update(T.root[x],1,aa[0],a[x],aa[a[x]]);
      	ans[x]=(ans[x]+T.tree[T.root[x]].ans*d%mod+mod)%mod;
      }
      void solve(int n,ll d,ll a[],ll aa[])
      {
      	T.clear();
      	for(int i=1;i<=n;i++)
      	{
      		aa[i]=a[i];
      	}
      	sort(aa+1,aa+1+n);
      	aa[0]=unique(aa+1,aa+1+n)-(aa+1);
      	for(int i=1;i<=n;i++)
      	{
      		a[i]=lower_bound(aa+1,aa+1+aa[0],a[i])-aa;
      	}
      	for(int i=1;i<=aa[0];i++)
      	{
      		aa[i]=(aa[i]+mod)%mod;
      	}
      	dfs(1,0,d,a,aa);
      }	
      int main()
      {
      	freopen("distance.in","r",stdin);
      	freopen("distance.out","w",stdout);
      	int n,u,v,i;
      	ll inv=qpow(2,mod-2,mod);
      	scanf("%d",&n);
      	for(i=1;i<=n-1;i++)
      	{
      		scanf("%d%d",&u,&v);
      		add(u,v);
      		add(v,u);
      	}
      	for(i=1;i<=n;i++)
      	{
      		scanf("%lld%lld",&a[i],&b[i]);
      		p[i]=a[i]+b[i];
      		q[i]=a[i]-b[i];
      	}
      	solve(n,1,a,aa);
      	solve(n,1,b,bb);
      	solve(n,(mod-inv)%mod,p,qq);
      	solve(n,(mod-inv)%mod,q,pp);
      	for(i=1;i<=n;i++)
      	{
      		printf("%lld\n",ans[i]*2%mod);
      	}
      	fclose(stdin);
      	fclose(stdout);
      	return 0;
      }
      

\(T4\) D. 团队选拔(selection) \(8pts\)

  • 部分分

    • 子任务 \(1\) :爆搜。

      点击查看代码
      const ll p=998244353;
      ll a[100010],vis[100010],ans[100010];
      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;
      }
      ll gcd(ll a,ll b)
      {
      	return b?gcd(b,a%b):a;
      }
      void dfs(ll pos,ll n,ll pre)
      {
      	if(pos==n+1)
      	{
      		ll flag=1,pos=0,d=0,last=0;
      		for(ll i=1;i<=n;i++)
      		{
      			if(vis[i]!=0)
      			{
      				last=gcd(last,a[i]);
      				if(vis[i]!=vis[i+1])
      				{
      					pos=i;
      					break;
      				}
      			}
      		}
      		for(ll i=pos+1;i<=n;i++)
      		{
      			if(vis[i]!=0)
      			{
      				d=gcd(d,a[i]);
      				if(vis[i]!=vis[i+1])
      				{
      					flag&=(d==0||d==last);
      					d=0;
      				}
      			}
      			else
      			{
      				flag&=(d==0||d==last);
      				d=0;
      			}
      		}
      		if(flag==1)
      		{
      			for(ll i=1;i<=n;i++)
      			{
      				if(vis[i]!=0)
      				{
      					ans[i]=(ans[i]+1)%p;
      				}
      			}
      		}
      	}
      	else
      	{
      		if(vis[pos-1]!=0)
      		{
      			vis[pos]=vis[pos-1];
      			dfs(pos+1,n,pos);
      		}
      		vis[pos]=vis[pre]+1;
      		dfs(pos+1,n,pos);	
      		vis[pos]=0;
      		dfs(pos+1,n,pre);
      	}
      }
      int main()
      {
      	freopen("selection.in","r",stdin);
      	freopen("selection.out","w",stdout);
      	ll n,i;
      	scanf("%lld",&n);
      	for(i=1;i<=n;i++)
      	{
      		scanf("%lld",&a[i]);
      	}
      	dfs(1,n,1);
      	for(i=1;i<=n;i++)
      	{
      		printf("%lld ",ans[i]);
      	}
      	fclose(stdin);
      	fclose(stdout);
      	return 0;
      }
      
    • 子任务 \(2\)

    • 子任务 \(3,4\)

      • 通过 luogu P3794 签到题IV 的经典结论,有当固定左端点 \(l\)\(\gcd\limits_{i=l}^{r} \{ a_{i} \}\) 至多会变化 \(O(\log V)\) 次。

    • 子任务 \(5\)

  • 正解

    点击查看官方题解代码
    #include<bits/stdc++.h>
    #define LL long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    inline int read() {
    	char c=getchar();int x=0;while(c<'0'||c>'9')c=getchar();
    	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    const int mod=998244353,maxn=100005;
    struct sode{int l,r,v;}st[maxn],bl[maxn];
    struct node{int l,r,x,v;}A[maxn*30],B[maxn*30];
    struct Seg{int l,r,v;}seg[2][maxn];
    int gcd(int a,int b) {return !b?a:gcd(b,a%b);}
    inline int dqm(int x) {return x<0?x+mod:x;}
    inline int qm(int x) {return x>=mod?x-mod:x;} 
    inline int cmp(const node &A,const node &B){
    	return A.v==B.v?A.x<B.x:A.v<B.v;
    }
    inline int ctp(const node &A,const node &B) {
    	return A.v==B.v?A.x>B.x:A.v<B.v;
    }
    int n,a[maxn],top,sa,sb,lp,sz[2],ans,pre[maxn];
    int l[maxn*3],r[maxn*3],tag[maxn*3],d[maxn*3];
    inline void pushup(int i) {d[i]=qm(d[i<<1]+d[i<<1|1]);}
    inline void pushr(int i,int v) {
    	tag[i]=v;d[i]=1ll*(r[i]-l[i]+1)*v%mod;
    }
    inline void pushdown(int i) {
    	if(tag[i]==-1) return;pushr(i<<1,tag[i]);
    	pushr(i<<1|1,tag[i]);tag[i]=-1;
    }
    void bud(int x,int y,int i) {
    	l[i]=x,r[i]=y,tag[i]=-1;if(x==y) return;
    	int mid=x+y>>1;bud(x,mid,i<<1),bud(mid+1,y,i<<1|1);
    }
    void chg(int x,int y,int v,int i) {
    	if(x<=l[i]&&y>=r[i]) {pushr(i,v);return;}
    	int mid=l[i]+r[i]>>1;pushdown(i);
    	if(x<=mid) chg(x,y,v,i<<1);
    	if(y>mid) chg(x,y,v,i<<1|1);pushup(i);
    }
    int qry(int x,int y,int i) {
    	if(x<=l[i]&&y>=r[i]) return d[i];
    	int mid=l[i]+r[i]>>1;pushdown(i);
    	return qm((x<=mid?qry(x,y,i<<1):0)+(y>mid?qry(x,y,i<<1|1):0));
    }
    inline void ins(int l,int r,int v,int o) {
    	if(r>n)r=n;if(l<1)l=1;if(l>r) return;
    	seg[o][++sz[o]]=(Seg){l,r,v};
    }
    inline void calc(int l,int r,int v) {
    	pre[l]=qm(pre[l]+v),pre[r+1]=dqm(pre[r+1]-v);
    } 
    inline void solve(int L,int R) {
    	int lst=0,v=0;sz[0]=sz[1]=0;
    	for(int i=L;i<=R;++i) {
    		chg(lst,A[i].x-1,v,1);
    		ins(lst+1,A[i].x,qm(v+1),0);
    		lst=A[i].x;
    		v=qm(v+qry(A[i].l-1,A[i].r-1,1));
    		v=qm(v+A[i].r-A[i].l+1);
    	}  
    	chg(lst,n,v,1);
    	ins(lst+1,n+1,qm(v+1),0);
    	lst=n+1,v=0;int rp=lp;
    	while(lp<=sb&&B[lp].v==A[L].v) ++lp;
    	for(int i=rp;i<lp;++i) {
    		chg(B[i].x+1,lst,v,1);
    		ins(B[i].x,lst-1,qm(v+1),1);
    		lst=B[i].x;
    		v=qm(v+qry(B[i].l+1,B[i].r+1,1));
    		v=qm(v+B[i].r-B[i].l+1);
    	}  
    	chg(1,lst,v,1);ans=qm(ans+v);
    	ins(0,lst-1,qm(v+1),1);
    	std::reverse(seg[1],seg[1]+sz[1]+1);
    	int x,y;x=0,y=0;
    	while(x<=sz[0]&&y<=sz[1]) {
    		calc(max(seg[0][x].l,seg[1][y].l),min(seg[0][x].r,seg[1][y].r),dqm(1ll*seg[0][x].v*seg[1][y].v%mod-1));
    		if(seg[0][x].r<seg[1][y].r) ++x;
    		else if(seg[0][x].r>seg[1][y].r) ++y;
    		else if(seg[0][x].r==seg[1][y].r) ++y,++x;
    	}
    }
    int main() {
    	freopen("selection.in", "r", stdin);
    	freopen("selection.out", "w", stdout);
    	n=read();
    	for(int i=1;i<=n;i++) a[i]=read();
    	for(int i=1;i<=n;i++) {
    		for(int j=1;j<=top;++j) 
    			st[j].v=gcd(st[j].v,a[i]);
    		st[++top]=(sode){i,i,a[i]};
    		int tot=0,x=i,y=i;
    		for(int j=top-1;j>=0;--j) {
    			if(st[j].v!=st[j+1].v) {
    				bl[++tot].l=x,bl[tot].r=y;
    				bl[tot].v=st[j+1].v;
    				x=st[j].l,y=st[j].r;
    			}else x=st[j].l;
    		}
    		top=0;
    		while(tot) st[++top]=bl[tot--];
    		for(int j=1;j<=top;++j) 
    			A[++sa]=(node){st[j].l,st[j].r,i,st[j].v};
    	}
    	top=0;
    	for(int i=n;i;i--) {
    		for(int j=1;j<=top;++j) 
    			st[j].v=gcd(st[j].v,a[i]);
    		st[++top]=(sode){i,i,a[i]};
    		int tot=0,x=i,y=i;
    		for(int j=top-1;j>=0;--j) 
    			if(st[j].v!=st[j+1].v) {
    				bl[++tot].l=x,bl[tot].r=y;
    				bl[tot].v=st[j+1].v;
    				x=st[j].l,y=st[j].r;
    			} else y=st[j].r;
    		top=0;
    		while(tot) st[++top]=bl[tot--];
    		for(int j=1;j<=top;++j) 
    			B[++sb]=(node){st[j].l,st[j].r,i,st[j].v};
    	}
    	std::sort(A+1,A+sa+1,cmp);std::sort(B+1,B+sb+1,ctp);
    	int L=1;lp=1;
    	bud(0,n+1,1);
    	for(int i=2;i<=sa+1;++i) 
    		if(A[i].v!=A[i-1].v) 
    			solve(L,i-1),L=i;
    	for(int i=1;i<=n;i++)pre[i]=qm(pre[i-1]+pre[i]);
    	for(int i=1;i<=n;i++)printf("%d ",dqm(ans-pre[i]));
    	return 0;
    }
    

总结

  • \(T1\) 边权 $ \le k$ 的边界点初始值写错了,而且误认为 \(k-\max\limits_{x \in S}\{ x \}=\max\limits_{x \in S} \{ k-x \}\) ,挂了 \(60pts\)
  • \(T2\) 写完后以为原来代码搞反了 \(p,q\) 的含义遂反转了过来,结果是原来代码没写反,挂了 \(9pts\)
  • \(T4\) 爆搜的时间用得太多了。

后记

  • \(T2\) 的大样例下发文件里夹杂着 \(T4\) 的大样例, \(T4\) 的大样例下发文件里不知道是个什么东西。后来不知道什么时候把下发文件换了下。
  • \(T1,T2,T4\) 均出自 [2024暑期交流小组] CSP-S & NOIP模拟赛 4 ,故直接搬的官方整个 PDF 题解; \(T3\) 单独下发了题解。
posted @ 2024-10-15 18:02  hzoi_Shadow  阅读(36)  评论(0编辑  收藏  举报
扩大
缩小