CF2135,2136游记

前言

CF2135/CF2136\(Div.1/Div.2\) 难度,场切 \(E\) 题,差 \(2\) 分钟切 \(F1\)
\(A,B\) 题卡了好久,其他题基本上都是迎刃而解

A.In the Dream

题面

\(t\) 组数据,每组数据:
给定 \(4\) 个整数 \(a,b,c,d\)
你要在一个整数对 \(p:(l,r)\) 上进行 \(2\) 次游戏:
每次游戏,若干次操作:

  • 交替选择 \(l\)\(r\) ,并令其增加至多 \(2\)
    初始时 \(p=(0,0)\) ,问能否在两次游戏后,使得 \(p\) 变为 \((a,b),(c,d)\) (两次游戏间可以连续选择 \(l,r\) )。

思路

显然,交换 \(a,b\) 使得 \(a>b\) 那么只要 \(a+2>2b\) 答案就是不。

实现

#include<iostream>
using namespace std;
int tt,a,b,c,d;
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>tt;
	while(tt--){
		cin>>a>>b>>c>>d;
		c-=a;d-=b;
		if(a<b)swap(a,b);
		if(c<d)swap(c,d);
		b*=2;a-=2;
		d*=2;c-=2;
		if(a>b||c>d)cout<<"No\n";
		else cout<<"Yes\n";
	}
	return 0;
}

B.Like the Bitset

题面

\(t\) 组数据,每组数据:
给定一个长度为 \(n\)\(01\) 字符串 \(s\) 与一个整数 \(k\)
你需要找到一个 \(1-n\) 的排列 \(a\) ,使得对于任意 \(s_i=1\) ,有:
\(\forall l\le i\le r,r-l+1\ge k\) 满足 \(a_i\ne \max(a_l,...,a_r)\)

思路

显然,只要存在一个全为 \(1\) 的长度不小于 \(k\)\(s\) 的子串,我们就无法构造对于排列。
而只要我们使得所有 \(s_i=1\)\(i\)\(s_i=0\)\(j\)\(a_i<a_j\) ,这个序列肯定满足条件。
那么我们只需要遍历 \(s_i\) ,若 \(s_i=0\) ,为 \(a_i\) 赋值当前最大值,否则赋值当前最小值。

实现

#include<iostream>
using namespace std;
const int N=2e5+5;
int tt,n,k,cnt;
string s;
void solve(){
	cin>>n>>k;
	cin>>s;
	s=" "+s;
	cnt=0;
	for(int i=1;i<=n;++i)if(s[i]=='1'){
		++cnt;
		if(cnt==k){cout<<"No\n";return;}
	}
	else cnt=0;
	int l=1,r=n;
	cout<<"Yes\n";
	for(int i=1;i<=n;++i)if(s[i]=='0')cout<<r--<<" ";else cout<<l++<<" ";cout<<endl;
}
int main(){
	cin>>tt;
	while(tt--)solve();
	return 0;
}

$ C.Against the Difference

题面

定义
若一个数列由 \(n\)\(n\) 组成,则称其为
当一个数列 \(A\) 满足以下条件中的一项,则称其为整洁数列:

  • \(A\)
  • \(A=BC\) ,其中 \(B,C\) 均为整洁数列 。

\(t\) 组数据,每组数据:
给定一个大小为 \(n\) 的数列。
求其最大整洁子序列长度。

思路

很显然的 DP ,思路和最长递增子序列差不多。
我们用队列 \(q_k\) 维护每个 \(a_i=k\)\(i\)
\(size(q_k)>k\) 时,弹出 \(j=top(q_k)\)
\(dp_i=dp_j+j\)
否则 \(dp_i=dp_{i-1}\)

实现

#include<iostream>
#include<queue>
using namespace std;
const int N=2e5+5;
int tt,n,x,f[N],ans;
queue<int>q[N];
void solve(){
	cin>>n;
	ans=0;
	for(int i=1;i<=n;++i){
		while(q[i].size())q[i].pop();
		f[i]=0;
	}
	for(int i=1;i<=n;++i){
		cin>>x;
		q[x].push(i);
		if(q[x].size()>=x){
			f[i]=f[q[x].front()-1]+x;
			ans=max(ans,f[i]);
			q[x].pop();
		}
		f[i]=max(f[i],f[i-1]);
	}
	cout<<ans<<endl;
}
int main(){
	cin>>tt;
	while(tt--)solve();
	return 0;
}

D.For the Champion

题面

交互题
\(t\) 组交互,每组交互:
你位于未知的坐标 \((X,Y)\) ,周围有 \(n\) 个锚点 \((x_i,y_i)\) ,你可以进行至多 \(10\) 次询问:

  • ? U k ,你的坐标变为 \((X,Y+k)\)
  • ? D k ,你的坐标变为 \((X,Y-k)\)
  • ? L k ,你的坐标变为 \((X-k,Y)\)
  • ? R k ,你的坐标变为 \((X+k,Y)\)

然后你将得到回答 \(\min(|x_i-X|+|y_i-Y|)\)
最终需要你定你的初始坐标 \((X,Y)\)
\(-10^9\le X,Y,x_i,y_i\le 10^9,0\le k\le 10^9\)

思路

我们发现,最烦人的是绝对值,我们只需要想怎么去掉就行了。
显然,当 \(X,Y\) 足够大(小)的时候, \(|x_i-X|=X-x_i(x_i-X)\)\(|y_i-Y|=Y-y_i(y_i-Y)\)
那么我们直接

? U 1e9
? U 1e9
? R 1e9
? R 1e9

\((X',Y')=(X+2e9,Y+2e9),X+2e9\ge-1e9+2e9=1e9\ge\max(x_i),Y+2e9\ge-1e9+2e9=1e9\ge\max(y_i)\)
将会得到回答 \(\min(|X+2e9-x_i|+|Y+2e9-y_i|)=\min(X+Y+4e9-(x_i+y_i))=X+Y+4e9-\min(x_i+y_i)\) 于是我们可以得到 \(X+Y\)
然后我们执行

? L 1e9
? L 1e9
? L 1e9
? L 1e9

同理可以得到 \(X-Y\)
于是我们就知道了 \(X,Y\)

实现

看了 jiangly 的实况后,修改了一下马蜂。

#include<iostream>
using namespace std;
#define ll long long
const int N=105;
int tt,n;
ll x[N],y[N],d1,d2,d3,d4;
ll query(char s){
	ll ret;
	cout<<"? "<<s<<" 1000000000\n";
	cin>>ret;
	return ret;
}
void answer(ll a,ll b){cout<<"! "<<a<<" "<<b<<endl;}
void solve(){
	d3=d4=-2e9;
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>x[i]>>y[i];
		if(x[i]+y[i]>d3)d3=x[i]+y[i];
		if(y[i]-x[i]>d4)d4=y[i]-x[i];
	}
	query('U');query('U');query('L');d1=query('L');
	query('R');query('R');query('R');d2=query('R');
	d1+=d4-4e9;d2+=d3-4e9;
	answer((d2-d1)/2,(d2+d1)/2);
}
int main(){
	cin>>tt;
	while(tt--)solve();
	return 0;
}

E.By the Assignment

题面

定义
对于一条简单路径 \([l_1,...,l_k]\) ,其值 \(d(l)=v_{l_1}\oplus...\oplus v_{l_k}\)
\(t\) 组数据,每组数据:
给定一个 \(n\) 个点 \(m\) 条边的无向图 \(G\) ,以及每个点的权值 \(v_i\) 和一个整数 \(V\)
其中,若 \(v_i=-1\) ,则 \(i\) 点可以赋值 \([0,V-1]\) 的任意整数。
求使得这个图满足:
对于任意点 \((u,v)\) ,以其为端点的每条路径的值相等。

思路

首先,如果,两个点之间只有一条简单路径,则对于路径上的点随意赋值即可。
而如果两个点之间有不止一条简单路径,则说明其路径上有环。
那么我们可以通过 Tarjan 判强连通分量,把点与环分开。
而对于一个强连通分量:
若其大小为 \(1\) ,其为点
否则其由至少 \(1\) 个环组成。
先不考虑怎么把在一个强连通分量中的环分开,我们讨论单个环:
显然,当环上所有节点的值均相等的时候,其才有可能满足条件。
简要分析可知:环的长度为偶数,其可取任意值;而环的长度为奇数,只能取 \(0\)
而将一个强连通分量拆成数个环,我们可以通过 DFS 求解:

假设当前搜索到 \(u\) ,遍历 \(u\) 的儿子 \(v\)
如果 \(v\) 已被搜过,则一定有个环 \((u,fa[u],...,lca(u,v),...,.fa[v],v)\)
我们记录 \(u\) 的深度 \(d[u]\)
那么 \(d[u]+d[v]\) 与环的长度同奇偶。
(当然我们也可以每次令 \(d[u]=d[fa[u]]\oplus 1\) ,用 \(d[u]\oplus d[v]\) 判断奇偶(看了 jiangly 的视频,原来这个方法是二分图))

实现

#include<iostream>
#include<vector>
using namespace std;
#define ll long long
const ll mod=998244353;
const int N=2e5+5;
int tt,n,m,V,v[N];
int DFN,dfn[N],low[N],co[N],col,s[N],top,sz[N],vc[N],pc[N],d[N];
bool mrk[N];
ll ans;
vector<int>g[N];
void Tarjan(int u,int ft){
	dfn[u]=low[u]=++DFN;
	s[++top]=u;
	for(auto v:g[u]){
		if(v==ft)continue;
		if(!dfn[v]){
			Tarjan(v,u);
			low[u]=min(low[u],low[v]);
		}
		else if(!co[v]){
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(low[u]==dfn[u]){
		co[u]=++col;
		sz[col]=1;
		pc[col]=u;
		while(s[top]!=u){
			co[s[top]]=col;
			--top;
			++sz[col];
		}
		--top;
	}
}
bool dfs(int u,int ft,int c){
	mrk[u]=1;d[u]=d[ft]+1;
	for(auto v:g[u]){
		if(v==ft)continue;
		if(co[v]!=c)continue;
		if(mrk[v])if((d[u]+d[v]+1)&1)return 1;else continue;
		if(dfs(v,u,c))return 1;
	}
	return 0;
}
void solve(){int x,y;
	cin>>n>>m>>V;
	ans=1;
	col=DFN=top=0;
	for(int i=1;i<=n;++i){
		cin>>v[i];
		co[i]=dfn[i]=mrk[i]=d[i]=0;
		vc[i]=-1;
		g[i].clear();
	}
	for(int i=1;i<=m;++i){
		cin>>x>>y;
		g[x].push_back(y);
		g[y].push_back(x);
	}
	for(int i=1;i<=n;++i)if(!dfn[i])Tarjan(i,0);
	for(int i=1;i<=n;++i){
		if(vc[co[i]]==-1)vc[co[i]]=v[i];
		else if(v[i]!=vc[co[i]]&&v[i]!=-1){cout<<"0\n";return;}
	}
	for(int i=1;i<=col;++i){
		if(sz[i]>1){
			if(dfs(pc[i],0,i)){
				if(vc[i]>0){cout<<"0\n";return;}
			}
			else if(vc[i]==-1)ans=ans*V%mod;
		}
		else if(vc[i]==-1)ans=ans*V%mod;
	}
	cout<<ans<<"\n";
}
int main(){
	cin>>tt;
	while(tt--)solve();
	return 0;
}

F1.From the Unknown(Easy Version)

题面

交互题
\(t\) 组交互,每组交互:
你需要确定一个整数 \(W(1\le W\le 10^5)\) ,你可以进行至多 \(2\) 次询问:

  • ? n a[1] ... a[n]\(1\le n\le 10^5\)

评测机会进行以下操作:

int fuc(){
	int l=0,s=0;
	for(int i=1;i<=n;++i){
		if(a[i]>W)return 0;
		if(s+a[i]<=W)s+=a[i];
		else{++l;s=a[i];}
	}
	return l;
}

思路

很显然,我们一开始直接 ? 100000 1 ... ,得到返回值 \(ret_1\)
那么 \(\displaystyle W\in[l,r],l=\left\lceil\frac{100000}{ret_1}\right\rceil,r=\left\lceil\frac{100000}{ret_1-1}\right\rceil-1\)
接下来我们需要一个个试探可能的 \(W\)
直接 ? 2*(r-l)+1 l l 1 ... l r-l ,那么对于 \(l+i<W\) 其会显示 \(1\) 行,否则显示 \(2\) 行。
于是 \(W=2r-l-ret_2+1\)

实现

代码是重构后的,写得易读一点。
#include<iostream>
#include<vector>
using namespace std;
#define ll long long
int tt,n=1e5,a,l,r;
int query(vector<int>vec){
	cout<<"? "<<vec.size()<<" ";
	for(auto x:vec)cout<<x<<" ";cout<<endl;
	int ret;
	cin>>ret;
	return ret;
}
void solve(){
	a=query(vector<int>(n,1));
	vector<int>b;
	l=(n+a-1)/a;
	r=a==1?n:(n-1)/(a-1);
	if(l==r){cout<<"! "<<l<<endl;return;}
	b.push_back(l);
	for(int i=l+1;i<=r;++i)b.push_back(l),b.push_back(i-l);
	a=query(b);
	cout<<"! "<<r+r-l-a+1<<endl;
}
int main(){
	cin>>tt;
	while(tt--)solve();
	return 0;
}

F2.From the Unknwon(Hard Version)

题面

和 F1 差不多,但是加了个 \(\sum n\le 2.5e4\) 的要求。

思路

我们在 F1 少用了一个条件,即为 \(\max(a_i)>W\) 时返回 \(0\)
利用这个条件我们就可以类似于分块地解决这题。
设第一次询问我们输出 \(B\) ,即 ? s B ... 其中 \(s=\left\lfloor\frac{N}{B}\right\rfloor\)
接下来分类讨论:

\(ret_1=0\)

那么 \(W<B\) ,显然我们只能输出全部为 \(1\) ,问题是要输出几个。
因为我们只剩下一次机会,所以需要 \(\forall i\le W,\left\lceil\frac{C}{i}\right\rceil\) 均不同。
那么取 \(C=B^2\) 即可(其实也可以取 \((B-1)^2\))。

否则 \(\displaystyle ret_1=\left\lceil\frac{N}{\left\lfloor\frac{W}{B}\right\rfloor}\right\rceil\)

那么 \(\displaystyle W\in[l,r],l=B\cdot\left\lfloor\frac{N-1}{ret_1}+1\right\rfloor,r=B\cdot\left\lfloor\frac{N-1}{ret_1-1}+1\right\rfloor-1\)
后同 F1 ,直接 ? 2*(r-l)+1 l l 1 ... l r-l+1 ,那么对于 \(l+i<W\) 其会显示 \(1\) 行,否则显示 \(2\) 行。
于是 \(W=2r-l-ret_2+1\)
然后,我们可以写个程序暴力求解,什么 \(N,B\) 最合适。
题解给出 \(N=11343,B=116\)
\(\sum n\le N+B^2=24799\)

实现

#include<iostream>
#include<vector>
using namespace std;
#define ll long long
int tt,N=11343,B=116,a,l,r;
int query(vector<int>vec){
	cout<<"? "<<vec.size()<<" ";
	for(auto x:vec)cout<<x<<" ";cout<<endl;
	int ret;
	cin>>ret;
	return ret;
}
void solve(){
	a=query(vector<int>(N,B));
	if(a==0){
		a=query(vector<int>(B*B,1));
		cout<<"! "<<(B*B-1)/(a-1)<<endl;
	}
	else{
		vector<int>b;
		l=B*(1+(N-1)/a);
		r=B*(1+(N-1)/(a-1))-1;
		r=min(100000,r);
		if(l==r){cout<<"! "<<l<<endl;return;}
		b.push_back(l);
		for(int i=l+1;i<=r;++i)b.push_back(l),b.push_back(i-l);
		a=query(b);
		cout<<"! "<<r+r-l-a+1<<endl;
	}

}
int main(){
	cin>>tt;
	while(tt--)solve();
	return 0;
}

\(Div.1\) 编号

后面的题就不是我的水平能解决的了。

E1.Beyond the Palindrome (Easy Version)

E2.Beyond the Palindrome (Hard Version)

F.To the Infinity

posted @ 2025-08-31 09:55  Xie2Yue  阅读(45)  评论(0)    收藏  举报