Educational CF round174(div2)总结
得分总结:场切4道罚时257rank1624
主要问题:
- B题没读懂题吃了罚时。
- A,B题多测都没有清空,各吃一发罚时。
- D题犯唐了,翻转后没有调用,吃四发罚时
简直不是人
更新:2.20 增加F题解
链接:A
我们考虑什么情况下 \(b\) 数组是不合法的,当出现 \(b_i\) 使得 \(b_{i-1}=b_{i+1}=1\) 且 \(b_i=0\) 时b是不合法的,因为 \(i\) 位与 \(i+1\) 和 \(i-1\)位明显矛盾了。
我们直接判断b的每一位,出现不合法就输出No。
AC记录
ACcode:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 110
// By flyfreemrn
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
ll b[MAXN];
ll n,T,ans;
void work(){
n=read()-2,ans=1;
memset(b,0,sizeof(b));
for(int i=1;i<=n;i++){
b[i]=read();
}
for(int i=1;i<=n;i++){
if(b[i-1]==1&&b[i+1]==1&&b[i]==0){
cout<<"NO\n";
return;
}
}
cout<<"YES\n";
return;
}
int main(){
T=read();
while(T--)work();
return 0;
}
链接:B
首先,题目所给的操作类似于黑白棋盘染色,这提示我们同一种颜色至多两次操作就能全部转化成另一种颜色,第一次选全部在白格上的,第二次选全部在黑格上的,就一定能补缺补漏的转化。
所以,对于每一种颜色,有两种情况。
- 没有相同颜色的格子相接,也就是所有这种颜色的格子都在黑格或白格上,对于这种颜色一次操作就能转化。
- 出现相同颜色的格子相接,我们需要两次操作转化。
因为最后要转化为同一种颜色,所以我们可以去除其中一种颜色的贡献,特判一下 \(-1\) 还是 \(-2\)。
AC记录
ACcode:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 1010
#define pir pair<ll,ll>
// By flyfreemrn
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
ll addx[4]={0,1,0,-1},addy[4]={1,0,-1,0};
ll n,m,T,sum,res;
ll col[MAXN][MAXN],vis[MAXN][MAXN];
map <ll,ll> t;
set <ll> st;
void work(){
n=read(),m=read();
sum=0,res=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)col[i][j]=read(),st.insert(col[i][j]),t[col[i][j]]=0;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int u=0;u<=3;u++){
ll edx=i+addx[u],edy=j+addy[u];
if(edx>n||edy>m||edx<1||edy<1)continue;
if(col[i][j]==col[edx][edy])t[col[i][j]]=1;
}
}
}
for(auto it=st.begin();it!=st.end();it++){
if(t[(*it)]){
sum+=2,res=2;
}else sum+=1;
}
cout<<sum-res<<endl;
st.clear();
return;
}
int main(){
T=read();
while(T--)work();
return 0;
}
先考虑暴力情况
如果有一个 \(1\) 和一个 \(3\) 中间隔了 \(x\) 个 \(2\) 的话,他们对答案的贡献显然等于
每找到一个 \(1\) 后,我们向后找所有的 \(3\) 并统计路径上 \(2\) 的个数,直接计入答案。
时间复杂度 \(O(n^2)\)。
优化
我们可以考虑将每一个序列的贡献都记在结尾的 \(3\) 上(当然也可以拆到 \(1\) 上)。
加入一个 \(3\) 前面有 \(P_i\) 个隔了 \(i\) 个 \(2\) 的 \(1\)。
那么他对答案的贡献就是
我们发现,这个值是可以递推计算的,我们记上式值为 \(sum\)。
当我们找到一个 \(2\) 时,有
那么每一个 \(1\) 对于答案的贡献都会 \(*2+1\)。
所以我们额外记当前位置 \(1\) 的个数为 \(cnt\)。
那么 \(sum\) 就有
当我们找到一个 \(1\) 时,有
当我们找到一个 \(3\) 时,直接把 \(sum\) 累加到答案即可。
最后别忘了取模。
AC记录
ACcode:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 998244353
#define MAXN 200010
// By flyfreemrn
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
ll cnt1,cnt2,sum;
ll n,T,ans;
void work(){
n=read(),ans=cnt1=cnt2=sum=0;
for(int i=1;i<=n;i++){
ll a=read();
if(a==1){
cnt1++;
// sum++;
}else if(a==2){
sum=(sum*2+cnt1)%mod;
}else{
ans=(ans+sum)%mod;
}
}
cout<<ans<<endl;
return;
}
int main(){
T=read();
while(T--)work();
return 0;
}
链接:D
回文串的题,我们先记中间位置为 \(mid\)(因为字符串长度为偶数,所以 \(mid\) 这个位置是不存在的,\(mid+1\) 和 \(mid-1\) 是相邻的)。
我们可以发现,原串中一些位置与对应的位置是相同的,这些位置不需要调整,而一些位置不相同,这些位置必须被调整。
所以我们找到 \(l,r\) 分别表示 \(mid+l\) 位置第一次出现不对应的情况,\(mid+r\) 位置最后一次出现不对应的情况。
接下来,我们分类讨论
- 找不到不对应的位置,原串就是回文的,直接输出 \(0\)。
- \(mid+l\) 到 \(mid+r\) 位置与 \(mid-r\) 到 \(mid-l\) 位置的字母数量完全相同,换句话说 \(mid+l\) 到 \(mid+r\) 是 \(mid-r\) 到 \(mid-l\) 的一个排序,这表示我们只需要调整 \(mid+l\) 到 \(mid+r\) 就可以了。
- 除此以外,调整的区间就必须包含 \(mid-r\) 到 \(mid-l\) 和 \(mid+l\) 到 \(mid+r\) 中的一个全部和另一个的一部分
我们先从右往左找,我们枚举区间左端点 \(i\) 让我们可以通过调整 \(i\) 到 \(mid+r\) 使原串回文,再从左往右找,这时候只需要反转一下原串然后重复一遍就可以了。
AC记录
ACcode:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 200010
// By flyfreemrn
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
char c[MAXN],s[MAXN];
ll l,r,n,t[30],T;
ll work(){
// n=read();
for(int i=1;i<=26;i++)t[i]=0;
// cin>>(c+1);
// n=strlen(c+1);
l=n,r=0;
for(ll i=1;i<=n/2;i++){
ll x=(n/2)-i+1,y=(n/2)+i;
if(c[x]==c[y])continue;
l=min(l,i),r=max(r,i);
}
if(l==n&&r==0){
// cout<<"0\n";
return 0;
}
for(int i=l;i<=r;i++){
ll x=(n/2)-i+1,y=(n/2)+i;
ll a=c[x]-'a'+1,b=c[y]-'a'+1;
t[a]--,t[b]++;
}
ll type=0;
for(int i=1;i<=26;i++){
if(t[i])type=1;
}
if(!type){
// cout<<r-l+1<<endl;
return r-l+1;
}
// for(int i=1;i<l;i++){
// ll x=(n/2)-i+1,y=(n/2)+i;
// ll a=c[x]-'a'+1,b=c[y]-'a'+1;
// t[a]++,t[b]++;
// }
for(int i=1;i<=r;i++){
ll x=(n/2)-i+1;
ll a=c[x]-'a'+1;
t[a]+=2;
type=0;
for(int j=1;j<=26;j++){
if(t[j]<0||t[j]%2){
type=1;
}
}
if(!type){
// cout<<i+r<<endl;
return i+r;
}
}
// cout<<2*r<<endl;
return 2*r;
}
int main(){
T=read();
while(T--){
cin>>(s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++)c[i]=s[i];
ll ans=work();
for(int i=1;i<=n;i++){
// swap(s[i])
c[i]=s[n-i+1];
}
ans=min(ans,work());
cout<<ans<<endl;
}
return 0;
}
链接:E
逆天分讨,不想写了(
链接:F
我们把题意转化一下,就是在 \(A\) 图和 \(B\) 图种加边删边,要求满足 \(B\) 图中连通的点 \(A\) 图中也要连通,问我们每次操作后至少还要加多少条边才能满足条件。
我们先考虑问题的简化版本。
假如现在只有加边操作,如何计算答案,因为我们要处理无向图的连通性问题,能想到并查集
先处理 \(B\) 图加边的操作,将 \(A\) 图存到并查集里,每次向 \(B\) 中加边时,如果两个点在 \(A\) 中不连通,就将答案++。
接下来我们处理 \(A\) 图的操作,同时为了防止以上操作重复计算答案,我们可以再开一个并查集,将我们前面加过的虚边也加进去。
这样,我们往 \(A\) 加边时先判断两点在第一个并查集中是否连通,再判断两点在第二个并查集中是否连通来统计答案。
同时,因为我们把虚边也存下来了,所以在 \(B\) 图中加边时也要考虑第二个并查集中的连通性。
正解
对于删边操作,我们可以转化一下,先对操作离线,假如我们在 \(i\) 时刻加入一条边,在 \(j\) 时刻删除这条边,就相当于这条边在 \(i-j\) 存在。
这就是很显然的线段树分治了,直接套上就能过了。
AC记录
ACcode:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 400010
#define pir pair<ll,ll>
#define dpir pair<pir,ll>
#define ls first
#define rs second
// By flyfreemrn
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
//回溯栈
struct node_modify{
ll pos,fa,siz,type;//0A图 1B图
}t[MAXN*10];
ll id;
//并查集
struct node_dsu{
ll fa[MAXN],siz[MAXN],type;
ll get(ll x){
return fa[x]==x?x:get(fa[x]);
}
void merge(ll x,ll y){//合并
x=get(x),y=get(y);
t[++id]=(node_modify){y,fa[y],siz[y],type};
t[++id]=(node_modify){x,fa[x],siz[x],type};
if(siz[x]>siz[y]){
fa[y]=x;
siz[x]+=siz[y];
}else{
fa[x]=y;
siz[y]+=siz[x];
}
}
void build(ll x){
for(int i=1;i<=x;i++)fa[i]=i,siz[i]=1;
}
}dsu[2];//0加虚边,1只存A图
//线段树分治
struct node_sgt{
ll l[MAXN*4],r[MAXN*4];
ll ans[MAXN];
vector <dpir> vec[MAXN*4];
void build(ll lz,ll rz,ll now){
l[now]=lz,r[now]=rz;
if(lz==rz)return;
ll mid=(lz+rz)>>1;
build(lz,mid,now<<1);
build(mid+1,rz,(now<<1)+1);
}
void insert(ll lz,ll rz,ll now,dpir val){//加边
if(l[now]>=lz&&r[now]<=rz){
vec[now].push_back(val);
return;
}
ll mid=(l[now]+r[now])>>1;
if(lz<=mid)insert(lz,rz,now<<1,val);
if(rz>mid)insert(lz,rz,(now<<1)+1,val);
}
void clear(ll stp){//回溯
while(id>stp){
dsu[t[id].type].fa[t[id].pos]=t[id].fa;
dsu[t[id].type].siz[t[id].pos]=t[id].siz;
id--;
}
}
void work(ll now,ll p){//处理
ll stp=id;
for(int i=0;i<vec[now].size();i++){
ll x=vec[now][i].ls.ls,y=vec[now][i].ls.rs,type=vec[now][i].rs;
if(type==1){
x=dsu[1].get(x),y=dsu[1].get(y);
if(x==y)continue;
if(dsu[0].get(x)!=dsu[0].get(y))p++;
dsu[1].merge(x,y);
}else{
x=dsu[0].get(x),y=dsu[0].get(y);
if(x==y)continue;
ll x1=dsu[1].get(x),y1=dsu[1].get(y);
if(x1==y1)p--;
else dsu[1].merge(x1,y1);
dsu[0].merge(x,y);
}
}
if(l[now]==r[now]){
ans[l[now]]=p;
clear(stp);
return;
}
work(now<<1,p);
work((now<<1)+1,p);
clear(stp);
}
}sgt;
//主函数
ll n,q,idx;
ll st[MAXN],ed[MAXN];
dpir line[MAXN];
map <pir,ll> mp[2];
int main(){
scanf("%lld%lld",&n,&q);
dsu[0].build(n),dsu[0].type=0;
dsu[1].build(n),dsu[1].type=1;
sgt.build(1,q,1);
for(int i=1;i<=q;i++){
char c;
ll x,y;
cin>>c>>x>>y;
ll type=(c=='B'?1:0);
if(mp[type][make_pair(x,y)]){
ll now=mp[type][make_pair(x,y)];
mp[type][make_pair(x,y)]=mp[type][make_pair(y,x)]=0;
ed[now]=i-1;
}else{
mp[type][make_pair(x,y)]=mp[type][make_pair(y,x)]=++idx;
line[idx]=make_pair(make_pair(x,y),type);
st[idx]=i;
}
}
for(int i=1;i<=idx;i++){
if(!ed[i])ed[i]=q;
sgt.insert(st[i],ed[i],1,line[i]);
}
sgt.work(1,0);
for(int i=1;i<=q;i++)cout<<sgt.ans[i]<<endl;
return 0;
}

浙公网安备 33010602011771号