据说我的 \(T2\) 乱搞硬控学长一上午?
A. White and Black
对于挨着的颜色相反的节点,肯定要每个点都转一次,手摸一下会发现,只要节点与父亲节点颜色不同就会产生一次贡献,但每
次 \(dfs\) 直接扫 \(O(n)\) 会 \(T\) ,所以我们需要去记录一下每个节点的儿子数,会发现对于节点和非父亲的祖先节点同时转变,每对
会比父子节点同时翻转多两次操作,所以我们把要翻转的点的父亲节点儿子数减2,节点的儿子数(白点)加点集数(黑点)即
为答案,多测记得把儿子复原
点击查看代码
#include<bits/stdc++.h>
const int maxn=2e5+10;
using namespace std;
int n,q,m,cnt[maxn],fa[maxn],v[maxn];
int main()
{
scanf("%d%d",&n,&q);
for(int i=2,x;i<=n;i++)
{
scanf("%d",&fa[i]);
cnt[fa[i]]++;
}
while(q--)
{
scanf("%d",&m);
int ans=m;
for(int i=1;i<=m;i++) scanf("%d",&v[i]),cnt[fa[v[i]]]-=2;
for(int i=1;i<=m;i++) ans+=cnt[v[i]];
for(int i=1;i<=m;i++) cnt[fa[v[i]]]+=2;
printf("%d\n",ans);
}
return 0;
}
/*
5 1
1 2 3 4
2 1 5
*/
B. White and White
当时 \(T1\) 暴力太费时,导致没时间了, \(T2\) 胡了个特殊性质又对着模数想着骗点分,然后就硬控学长两小时,据说一开始好像
\(A\) 了。。。后来学长被迫改数据又加了个捆绑测试给我卡了。这个乱胡的算法原理实际上是,在极多数中分很小的块数求和再
膜一个很小的模数,极大概率可以找到很多膜模数接近于0的块,所以答案极大概率是比模数小的,因为模数很小,样本很多,
所以膜出来的数也有相当多的是重复的,所以加起来直接膜是可以骗到很多分的
正解:区间\(dp\) ,用一个数组记录到目前的最小答案的下标,作为后面求解时的断点
点击查看代码
#include<bits/stdc++.h>
const int maxn=5e5+10;
using namespace std;
int n,k,p,a[maxn],sum[maxn],f[maxn][110],g[2][110];
int main()
{
scanf("%d%d%d",&n,&k,&p);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]+=a[i]+sum[i-1];
sum[i]%=p;
}
memset(f,0x7f7f7f,sizeof f);
f[0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=k;j++)f[i][j]=f[g[i&1][j-1]][j-1]+((sum[i]-sum[g[i&1][j-1]])%p+p)%p;
for(int j=1;j<=k;j++)g[i&1^1][j]=f[i][j]<f[g[i&1][j]][j]?i:g[i&1][j];
}
printf("%d",f[n][k]);
return 0;
}
C. Black and Black
这题写的时候也挺抽象的,本来是冲着那 \(20pts\) 的部分分去的,然后自己开始手模找规律,结果好像把正解写出来了。。。
赛事写时判无解时出了点小锅,就那20分部分分没拿到。。。
对于首尾元素是什么来考虑,分为首尾分别 \(1/-1\) 来考虑,里面再分奇偶考虑,首尾相同和 \(n\) 为奇数时肯定有解
对于构造,我们先让其由中间为0,按递增向两边差为一的填数,把首尾是一的位置留一个出来,用0减去现在填了的值
看是否合法,合法就输出,不合法就从n向前找一个位置让这个段的 \(a\) 序列的和为 \(1/-1\) (1在首就找为1的,反之则-1)
找不到就无解,然后一个让序列同时加,直到最后一个数合法了即可。
但有可能要填的数都比0要小,这样填出来相当于和比0多了\(sum(n)\)的整数倍(整个序列同时加的),所以判时还要判一个填
后的和是否大于零,\(sum(n)\)是不是等于0,都满足才是无解
点击查看代码
#include<bits/stdc++.h>
const int maxn=2e5+10;
using namespace std;
int n,a[maxn],b[maxn],sum[maxn];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
if(n==1)
{
printf("Yes\n");
printf("0\n");
return 0;
}
if(a[1]==1&&a[n]==1)
{
if(n&1)
{
b[(n+1)>>1]=0;
for(int i=((n+1)>>1)-1;i>=1;i--) b[i]=b[i+1]-1;
for(int i=((n+1)>>1)+1;i<n;i++) b[i]=b[i-1]+1;
int temp=0;
for(int i=1;i<n;i++) temp+=a[i]*b[i];
temp=-temp;
if(temp<=b[n-1])
{
while(temp<=b[n-1])
{
temp++,b[1]--;
}
}
b[n]=temp;
printf("Yes\n");
for(int i=1;i<=n;i++)
printf("%d ",b[i]);
}
else
{
b[n>>1]=0,b[(n>>1)+1]=1;
for(int i=(n>>1)-1;i>=1;i--) b[i]=b[i+1]-1;
for(int i=(n>>1)+2;i<n;i++) b[i]=b[i-1]+1;
int temp=0;
for(int i=1;i<n;i++) temp+=a[i]*b[i];
temp=-temp;
if(temp<=b[n-1])
{
while(temp<=b[n-1])
{
temp++,b[1]--;
}
}
b[n]=temp;
printf("Yes\n");
for(int i=1;i<=n;i++)
printf("%d ",b[i]);
}
}
if(a[1]==-1&&a[n]==-1)
{
if(n&1)
{
b[(n+1)>>1]=0;
for(int i=((n+1)>>1)-1;i>1;i--) b[i]=b[i+1]-1;
for(int i=((n+1)>>1)+1;i<=n;i++) b[i]=b[i-1]+1;
int temp=0;
for(int i=2;i<=n;i++) temp+=a[i]*b[i];
if(temp>=b[2])
{
while(temp>=b[2])
{
temp--,b[n]++;
}
}
b[1]=temp;
printf("Yes\n");
for(int i=1;i<=n;i++)
printf("%d ",b[i]);
}
else
{
b[n>>1]=0,b[(n>>1)+1]=1;
for(int i=(n>>1)-1;i>1;i--) b[i]=b[i+1]-1;
for(int i=(n>>1)+2;i<=n;i++) b[i]=b[i-1]+1;
int temp=0;
for(int i=2;i<=n;i++) temp+=a[i]*b[i];
if(temp>=b[2])
{
while(temp>=b[2])
{
temp--,b[n]++;
}
}
b[1]=temp;
printf("Yes\n");
for(int i=1;i<=n;i++)
printf("%d ",b[i]);
}
}
if(a[1]==-1&&a[n]==1)
{
if(n&1)
{
b[(n+1)>>1]=0;
for(int i=((n+1)>>1)-1;i>=1;i--) b[i]=b[i+1]-1;
for(int i=((n+1)>>1)+1;i<n;i++) b[i]=b[i-1]+1;
int temp=0,l=0,z=-1;
for(int i=1;i<n;i++) temp+=a[i]*b[i];
temp=-temp;
if(temp<=b[n-1])
{
for(int i=n-1;i>=0;i--)
{
if(sum[n]-sum[i]<0)
{
z=i;
break;
}
}
while(temp<=b[n-1])
{
temp++;
l++;
}
}
b[n]=temp;
printf("Yes\n");
int summ=0;
for(int i=1;i<=n;i++)
{
if(i>z) b[i]+=l;
summ+=b[i]*a[i];
}
if(summ!=0)
{
summ=summ/sum[n];
}
for(int i=1;i<=n;i++)
{
printf("%d ",b[i]-summ);
}
}
else
{
b[n>>1]=0,b[(n>>1)+1]=1;
for(int i=(n>>1)-1;i>=1;i--) b[i]=b[i+1]-1;
for(int i=(n>>1)+2;i<n;i++) b[i]=b[i-1]+1;
int temp=0,l=0,z=-1;
for(int i=1;i<n;i++) temp+=a[i]*b[i];
temp=-temp;
if(temp<=b[n-1])
{
for(int i=n-1;i>=0;i--)
{
if(sum[n]-sum[i]<0)
{
z=i;
break;
}
}
while(temp<=b[n-1])
{
temp++;
l++;
}
}
b[n]=temp;
int summ=0;
for(int i=1;i<=n;i++)
{
if(i>z) b[i]+=l;
summ+=b[i]*a[i];
}
if(z==-1&&summ!=0&&!sum[n])
{
printf("No\n");
return 0;
}
if(summ!=0)
{
summ=summ/sum[n];
}
printf("Yes\n");
for(int i=1;i<=n;i++)
{
printf("%d ",b[i]-summ);
}
}
}
if(a[1]==1&&a[n]==-1)
{
if(n&1)
{
b[(n+1)>>1]=0;
for(int i=((n+1)>>1)-1;i>1;i--) b[i]=b[i+1]-1;
for(int i=((n+1)>>1)+1;i<=n;i++) b[i]=b[i-1]+1;
int temp=0,l=0,z=-1;
for(int i=2;i<=n;i++) temp+=a[i]*b[i];
temp=-temp;
if(temp>=b[2])
{
for(int i=n-1;i>=0;i--)
{
if(sum[n]-sum[i]>0)
{
z=i;
break;
}
}
while(temp>=b[2])
{
temp--;
l++;
}
}
b[1]=temp;
printf("Yes\n");
int summ=0;
for(int i=1;i<=n;i++)
{
if(i>z) b[i]+=l;
summ+=b[i]*a[i];
}
if(summ!=0)
{
summ=summ/sum[n];
}
for(int i=1;i<=n;i++)
{
printf("%d ",b[i]-summ);
}
}
else
{
b[n>>1]=0,b[(n>>1)+1]=1;
for(int i=(n>>1)-1;i>1;i--) b[i]=b[i+1]-1;
for(int i=(n>>1)+2;i<=n;i++) b[i]=b[i-1]+1;
int temp=0,l=0,z=-1;
for(int i=2;i<=n;i++) temp+=a[i]*b[i];
temp=-temp;
if(temp>=b[2])
{
for(int i=n-1;i>=0;i--)
{
if(sum[n]-sum[i]>0)
{
z=i;
break;
}
}
while(temp>=b[2])
{
temp--;
l++;
}
}
b[1]=temp;
int summ=0;
for(int i=1;i<=n;i++)
{
if(i>z) b[i]+=l;
summ+=b[i]*a[i];
}
if(z==-1&&summ!=0&&!sum[n])
{
printf("No\n");
return 0;
}
if(summ!=0)
{
summ=summ/sum[n];
}
printf("Yes\n");
for(int i=1;i<=n;i++)
{
printf("%d ",b[i]-summ);
}
}
}
return 0;
}
/*
7
-1 -1 1 1 -1 1 1
*/
D. Black and White
首先dfs整棵树一遍,进入一个节点的时候加上一个左括号,然后是节点编号,当这个节点的所有子树遍历完后再添上一个右括号,这
就是括号序列。两个点的距离就是两点编号中去除可配对括号剩下的括号数。用线段树去维护
我们记右括号数为a,左括号数为b。左区间右左括号数记为\(a1,b1\),右区间\(a2,b2\)
\(dis=a1+abs(b1-a2)+b2=max((a1+b1)+(b2-a2),(a1-b1)+(a2+b2))\)
显然\(max(a1+b1),max(b2-a2)\)这种是可以单独维护的。
因此记录区间前缀的\(max(a+b),max(b-a)\),后缀的\(max(a+b),max(a-b)\)即可,用l,r分别表示,
点击查看代码
#include<bits/stdc++.h>
#define lid id<<1
#define rid id<<1|1
using namespace std;
const int maxn=1e5+10;
int num,s[maxn<<2],pos[maxn<<4],head[maxn],to[maxn<<1],nxt[maxn<<1],n,m,cnt,tot;
bool c[maxn];
void add(int x,int y)
{
to[++num]=y;
nxt[num]=head[x];
head[x]=num;
}
struct lsx
{
int a,b,l1,l2,r1,r2,dis;
}tr[maxn<<4];
void dfs(int u,int fa)
{
s[++tot]=-1;
s[++tot]=u;
pos[u]=tot;
for(int i=head[u];i;i=nxt[i])
{
int y=to[i];
if(y==fa)continue;
dfs(y,u);
}
s[++tot]=-2;
}
int max(int a,int b){return a>b?a:b;}
void merge(int id)
{
if(tr[lid].b>tr[rid].a)
tr[id].a=tr[lid].a,tr[id].b=tr[lid].b-tr[rid].a+tr[rid].b;
else
tr[id].a=tr[lid].a+tr[rid].a-tr[lid].b,tr[id].b=tr[rid].b;
tr[id].l1=max(tr[lid].l1,max(tr[rid].l1+tr[lid].a-tr[lid].b,tr[rid].l2+tr[lid].a+tr[lid].b));
tr[id].l2=max(tr[lid].l2,tr[rid].l2-tr[lid].a+tr[lid].b);
tr[id].r1=max(tr[rid].r1,max(tr[lid].r1-tr[rid].a+tr[rid].b,tr[lid].r2+tr[rid].a+tr[rid].b));
tr[id].r2=max(tr[rid].r2,tr[lid].r2+tr[rid].a-tr[rid].b);
tr[id].dis=max(max(tr[lid].r1+tr[rid].l2,tr[lid].r2+tr[rid].l1),max(tr[lid].dis,tr[rid].dis));
}
void build(int id,int l,int r)
{
if(l==r)
{
tr[id].a=tr[id].b=0;tr[id].l1=tr[id].l2=tr[id].r1=tr[id].r2=tr[id].dis=-1e9;
if(s[l]==-1)tr[id].b=1;
else if(s[l]==-2)tr[id].a=1;
else if(!c[s[l]])tr[id].l1=tr[id].r1=tr[id].r2=tr[id].l2=0;
return;
}
int mid=l+r>>1;
build(lid,l,mid);
build(rid,mid+1,r);
merge(id);
}
void modify(int id,int l,int r,int x)
{
if(l==r)
{
tr[id].a=tr[id].b=0;tr[id].l1=tr[id].l2=tr[id].r1=tr[id].r2=tr[id].dis=-1e9;
if(s[l]==-1)tr[id].b=1;
else if(s[l]==-2)tr[id].a=1;
else if(!c[s[l]])tr[id].l1=tr[id].r1=tr[id].r2=tr[id].l2=0;
return;
}
int mid=l+r>>1;
if(x<=mid)modify(lid,l,mid,x);
else modify(rid,mid+1,r,x);
merge(id);
}
int main()
{
scanf("%d",&n);
for(int i=1,u,v;i<n;i++)
{
scanf("%d%d",&u,&v);
add(u,v),
add(v,u);
}
dfs(1,0);
cnt=n;
build(1,1,tot);
scanf("%d",&m);
for(int i=1,x;i<=m;i++)
{
char s[2];
scanf("%s",s);
if(s[0]=='C')
{
scanf("%d",&x),cnt+=c[x]?1:-1;
c[x]^=1,modify(1,1,tot,pos[x]);
}
else if(cnt==0)printf("-1\n");
else if(cnt==1)printf("0\n");
else printf("%d\n",tr[1].dis);
}
}