CodeForces 1391 - Codeforces Round #663 (Div. 2)
昨晚被我妈强制睡觉了……今天来打个vp(不缺时效性)
CF比赛页面传送门
A - Suborrays
洛谷题目页面传送门 & CF题目页面传送门
给定\(n\),构造出一个\(1\sim n\)的排列\(a\)使得对于每个区间\([l,r]\)都满足\(\mathop{\operatorname{or}}\limits_{i=l}^ra_i\geq r-l+1\)。本题多测。
\(n\in[1,100]\)。最多\(100\)组数据。
天然D2A。
显然\([1,2,\cdots,n]\)满足。
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int testnum;
cin>>testnum;
while(testnum--){
int n;
cin>>n;
for(int i=1;i<=n;i++)cout<<i<<" ";
puts("");
}
}
B - Fix You
题目名称->f**k you
洛谷题目页面传送门 & CF题目页面传送门
不想翻译了,紫帆吧。
注意到显然最后一列全要是\(\texttt D\),最后一行全要是\(\texttt R\),因为否则就走出去了。改完之后发现已经符合题意了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=100,M=N;
int n,m;
char a[N+1][M+5];
void mian(){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i]+1;
int cnt=0;
for(int i=1;i<=n;i++)cnt+=a[i][m]!='D'&&a[i][m]!='C';
for(int i=1;i<=m;i++)cnt+=a[n][i]!='R'&&a[n][i]!='C';
cout<<cnt<<"\n";
}
int main(){
int testnum;
cin>>testnum;
while(testnum--)mian();
return 0;
}
C - Cyclic Permutations
洛谷题目页面传送门 & CF题目页面传送门
紫帆。
观察题面中的那个例子,发现若有相对大小关系与\([2,1,3]\)或\([3,1,2]\)相同的子段的话,一定是有环的。考虑否定,这样只能有单调上升、单调下降、单峰三种长度为\(3\)的子段,不难发现这样子的排列一定是单峰的。然后稍微看一下就会发现这样是无环的。于是有相对大小关系与\([2,1,3]\)或\([3,1,2]\)相同的子段是有环的充要条件。
直接算不会,考虑反面,就是求单峰排列的数量。枚举峰的位置,在\(i\)处时显然有\(\dbinom{n-1}{i-1}\)种,于是总共有\(\sum\limits_{i=1}^{n}\dbinom{n-1}{i-1}=2^{n-1}\)种。答案为\(n!-2^{n-1}\)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int n;
int main(){
cin>>n;
int ans1=1,ans2=1;
for(int i=1;i<=n;i++)ans1=1ll*ans1*i%mod;
for(int i=1;i<n;i++)(ans2<<=1)%=mod;
cout<<((ans1-ans2)%mod+mod)%mod;
return 0;
}
D - 505
想起了我的某个blog《404》
洛谷题目页面传送门 & CF题目页面传送门
这个好翻,翻一下(说到底还是一条懒狗)
给定一个\(n\times m\)的01矩阵,求至少要改多少个元素,使得每个边长为偶数的正方形都包含奇数个\(1\)。或报告无解。
\(nm\in\left[1,10^6\right]\)。
容易发现,若存在边长为\(4\)的正方形,肯定是无解的。因为将它拆成\(4\)个边长为\(2\)的正方形,小学老师教过奇+奇+奇+奇=偶。
现在只需要考虑\(n\leq 3\)或\(m\leq 3\)的情况。若\(m\leq 3\),将\(n,m\)颠倒一下。
- \(n=1\):显然答案为\(0\);
- \(n=2\):每列的\(1\)的数量的奇偶性显然只有\([1,0,1,0,\cdots]\)和\([0,1,0,1,\cdots]\)两种,都算一下比个大小即可;
- \(n=3\):每列上下两个\(2\times1\)矩阵的\(1\)的数量的奇偶性显然只有\(\begin{bmatrix}1&0&1&0&\cdots\\1&0&1&0&\cdots\end{bmatrix},\begin{bmatrix}0&1&0&1&\cdots\\1&0&1&0&\cdots\end{bmatrix},\begin{bmatrix}1&0&1&0&\cdots\\0&1&0&1&\cdots\end{bmatrix},\begin{bmatrix}0&1&0&1&\cdots\\0&1&0&1&\cdots\end{bmatrix}\)四种,易证每列最多修改\(1\)次,都算一下比个大小即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=1000000;
int n,m;
vector<string> v;
bool a[4][N+1];
int main(){
cin>>n>>m;
if(n>=4&&m>=4)return puts("-1"),0;
for(int i=1;i<=n;i++){
string a;
cin>>a;
v.pb(a);
}
if(n>=4){//翻转一下
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)a[j][i]=v[i-1][j-1]^48;
swap(n,m);
}
else{
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)a[i][j]=v[i-1][j-1]^48;
}
// for(int i=1;i<=n;i++){for(int j=1;j<=m;j++)cout<<a[i][j];puts("");}
if(n==1)puts("0");
else if(n==2){
int ans=0;
for(int i=1;i<=m;i++)ans+=(a[1][i]^a[2][i])==(i&1);
cout<<min(ans,m-ans);
}
else{
int ans1=0,ans2=0,ans3=0,ans4=0;//四种
for(int i=1;i<=m;i++)ans1+=(a[1][i]^a[2][i])!=(i&1)||(a[2][i]^a[3][i])!=(i&1);
for(int i=1;i<=m;i++)ans2+=(a[1][i]^a[2][i])==(i&1)||(a[2][i]^a[3][i])!=(i&1);
for(int i=1;i<=m;i++)ans3+=(a[1][i]^a[2][i])!=(i&1)||(a[2][i]^a[3][i])==(i&1);
for(int i=1;i<=m;i++)ans4+=(a[1][i]^a[2][i])==(i&1)||(a[2][i]^a[3][i])==(i&1);
cout<<min(min(ans1,ans2),min(ans3,ans4));
}
return 0;
}
E - Pairs of Pairs
这是我所看了题解的题目。看完题解还厚颜无耻地在vp结束之前写了交上去
洛谷题目页面传送门 & CF题目页面传送门
给定一个\(n\)个点\(m\)条边的连通无向图。你需要做以下两件事中的任意一件:
- 找一条长度至少为\(\left\lceil\dfrac n2\right\rceil\)的简单路径;
- 将至少\(\left\lceil\dfrac n2\right\rceil\)个节点(要求是偶数个)两两分组,满足任意两组的四个节点的导出子图最多有两条边。
可以证明至少有一件事是可以做的。
本题多测。
\(\sum n\in\left[2,5\times10^5\right],\sum m\in\left[1,10^6\right]\)。
无向连通图有个重要的东西:DFS树。它有一个很重要的性质:两个点之间有边仅当它们是长辈与晚辈的关系。很好证,因为若有非长晚辈关系的点对之间有边,那么当DFS到其中那个时间戳较小的点时,根据深度优先一定可以走到时间戳较大的那个点,从而使得它们是长晚辈关系,与假设矛盾。这与有向图DFS树的横叉边的性质类似。
看到第二件事中有要限制一些边不能出现这种感觉,不难想到DFS树的这个性质。(这个大概是Tarjan求DCC时要用到的,然鹅我还不会,所以没想到)
求出任意DFS树,如果存在深度\(\geq\left\lceil\dfrac n2\right\rceil\)的点那么显然可以做第一件事,直接输出就行了。否则,
注意到DFS树的这一性质,如何把它用起来。考虑两对深度不同的兄弟节点,兄弟之间显然不会有边,那么只有在不同对中的边。考虑从下往上连,由于只能连向祖先,而上面两个点是兄弟可以推出对于下面每个点最多只有上面的一个点作为它的祖先,于是最多会有两条边。
于是问题就迎刃而解了。接下来同层的尽量分组,每层最多留下一个单身。由于最大深度\(<\left\lceil\dfrac n2\right\rceil\),单身的也是小于这么多,可以保证被分组的点的数量。
并不能保证下次遇到这样的智商题能做出来哦(
多测memset
,爆零两行泪。
代码:
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=500000;
int n,m;
vector<int> nei[N+1];
bool vis[N+1];
int fa[N+1],dep[N+1];
void dfs(int x=1){//求搜索树
vis[x]=true;
for(int i=0;i<nei[x].size();i++){
int y=nei[x][i];
if(vis[y])continue;
fa[y]=x;dep[y]=dep[x]+1;
dfs(y);
}
}
vector<int> buc[N+1];
void mian(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)nei[i].clear(),buc[i].clear(),vis[i]=fa[i]=dep[i]=0;
while(m--){
int x,y;
scanf("%d%d",&x,&y);
nei[x].pb(y);nei[y].pb(x);
}
dep[1]=1;
dfs();
for(int i=1;i<=n;i++)
if(dep[i]>=n+1>>1){//可以输出简单路径了
puts("PATH");
printf("%d\n",dep[i]);
while(i)printf("%d ",i),i=fa[i];
puts("");
return;
}
else buc[dep[i]].pb(i);
puts("PAIRING");//不能输出简单路径,那么一定能分组
int cnt=0;
for(int i=1;i<=n;i++)cnt+=buc[i].size()>>1;//统计数量
printf("%d\n",cnt);
for(int i=1;i<=n;i++){//枚举层
for(int j=0;j+1<buc[i].size();j+=2)printf("%d %d\n",buc[i][j],buc[i][j+1]);//同层分组
}
}
int main(){
int testnum;
cin>>testnum;
while(testnum--)mian();
return 0;
}