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;
}
题面
定义
若一个数列由 \(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;
}
题面
定义
对于一条简单路径 \([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\) 编号
后面的题就不是我的水平能解决的了。

浙公网安备 33010602011771号