Codeforces Round #754 (Div. 2) 部分题解
A
考虑差值\(|a_1+a_3-2a_2|\),每次操作对其的影响为\(\pm 3\),所以只需要判断差值是否是\(3\)的倍数。
int main()
{
int T=read();
while (T--)
{
int a=read(),b=read(),c=read();
int x=abs(a+c-b-b);
if (x%3) puts("1");else puts("0");
}
return 0;
}
B
操作的关键是将前面的1和后面的0进行交换,贪心的将这些操作合并即可构造出一种最小方案。
int n,cnt0,b[N],m;
vi ans[N];
char s[N];
bool chk()
{
rep(i,1,cnt0) if (s[i]!='0') return 0;
return 1;
}
int main()
{
int T=read();
while (T--)
{
n=read();cnt0=0;
scanf("%s",s+1);
rep(i,1,n) cnt0+=(s[i]=='0');
m=0;
while (!chk())
{
m++;int len=0;
int l=1,r=n;
while (l<r)
{
while ((l<=n) && (s[l]=='0')) l++;
while ((r>=1) && (s[r]=='1')) r--;
if (l>=r) break;
b[++len]=l;b[++len]=r;
l++;r--;
}
for (int i=1;i<=len;i+=2) swap(s[b[i]],s[b[i+1]]);
for (int i=1;i<=len;i+=2) ans[m].pb(b[i]);
for (int i=len;i>=1;i-=2) ans[m].pb(b[i]);
}
printf("%d\n",m);
rep(i,1,m)
{
int len=ans[i].size();
printf("%d ",len);
rep(j,0,len-1) printf("%d ",ans[i][j]);puts("");
ans[i].clear();
}
}
return 0;
}
C
通过手玩可以发现:最小的合法串不会很长(最长的串为abbacca
或accabba
),所以直接枚举所有长度\(\leq 7\)的子串即可。
int cnt[N];
int main()
{
int T=read();
while (T--)
{
string s;int n;cin >> n >> s;
int ans=n+1;
rep(i,0,n-1)
{
cnt['a']=cnt['b']=cnt['c']=0;
cnt[s[i]]++;
rep(j,1,7)
{
if (i+j==n) break;
cnt[s[i+j]]++;
if ((cnt['a']>cnt['b']) && (cnt['a']>cnt['c']))
{
ans=min(ans,j+1);
break;
}
}
}
if (ans>n) ans=-1;
printf("%d\n",ans);
}
return 0;
}
D
根据Div2D不会很难知道这个题肯定会有简明结论
给出结论:存在一种构造方案使得所有边都会断开,从而先手无论选哪个点都能获得胜利。接下来通过给出构造方案来证明这一点。
我们的构造方案要满足\(\forall (u,v)\in E,u \ \mathrm{xor} \ v>\min(u,v)\),不妨设\(u>v\),考虑\(u\)的二进制的最高位,不难发现原条件等价于在这一位上\(v\)只能为\(0\)。故\(\forall (u,v)\in E\),我们要求\(u,v\)两数在二进制下的位数不同。
接下来考虑这个方案能否实现,将\(1\sim n\)拆分成\([2^0,2^1),[2^1,2^2),\cdots,[2^{p-1},2^p),[2^p,n]\)这\(p+1\)个部分,从而不同部分的数在而今之下的位数不同,且除最后一部分外第\(i\)部分有\(2^{i-1}\)个点。
由于树是一张二分图,我们可以将其黑白染色。对点数较少(\(\leq \frac{n}{2}\))的那半边点,我们可以将其二进制拆分,使得它能被上面的前\(p\)个部分的一个子集覆盖掉。我们再将剩下的部分分给另半边的点即可。
struct node{int to,nxt;}sq[N<<1];
int all=0,head[N];
int n,ans[N],p0[N],p1[N],r0,r1,x[N];
void addedge(int u,int v)
{
all++;sq[all].to=v;sq[all].nxt=head[u];head[u]=all;
}
void dfs(int u,int fu,int c)
{
if (c) p1[++r1]=u;else p0[++r0]=u;
go(u,i)
{
if (v==fu) continue;
dfs(v,u,c^1);
}
}
int main()
{
int T=read();
while (T--)
{
n=read();
rep(i,1,n-1)
{
int u=read(),v=read();
addedge(u,v);addedge(v,u);
}
r0=0;r1=0;
dfs(1,0,0);
int p=0;
while (1)
{
x[p+1]=(1<<p);p++;
if (x[p]>n) {x[p]=n+1;break;}
}
per(i,p,1)
{
int l=x[i-1],r=x[i]-1,len=r-l+1;
if (r0>=len)
{
rep(j,l,r)
{
ans[p0[r0]]=j;r0--;
}
}
else
{
rep(j,l,r)
{
ans[p1[r1]]=j;r1--;
}
}
}
rep(i,1,n) printf("%d ",ans[i]);puts("");
all=0;
rep(i,1,n) head[i]=0;
}
return 0;
}
E
所有的\(a_i\to 0,b_i\to b_i-a_i\)的问题和原问题等价。
先考虑对于给定的\(b_1\)怎么做。不难想到一个贪心做法:从1开始向后遍历,由于当前位置的数只能被当前位置的操作影响到,所以在这个位置的操作可以计算得到。根据调和级数知暴力做操作的复杂度是\(O(n\log n)\)的。
考虑\(b_1\)发生变动时的情况:假设当前的\(b_1\)为\(x\),那么照搬上面的做法可以得到第\(i\)次的操作是一个一次函数\(c_ix+d_i\)。最后的答案就是令\(x=x_0\)后计算\(\sum |c_ix_0+d_i|\)。可以先预处理出所有的一次函数,去除所有的常函数后将剩下的按零点从小到大排序。那么对于一次询问的答案必然是前半边取相反数而后半边不懂,这个分界点可以直接二分得到。
struct Poly{
ll a,b;
Poly(ll _a=0,ll _b=0) {a=_a;b=_b;}
};
Poly operator +(Poly a,Poly b)
{
return Poly(a.a+b.a,a.b+b.b);
}
Poly operator -(Poly a,Poly b)
{
return Poly(a.a-b.a,a.b-b.b);
}
int n,a[N],b[N],m;
Poly p[N],nowp[N],pre[N],suf[N],pp[N];
bool cmp(Poly x,Poly y) {return x.b*y.a<x.a*y.b;}
int main()
{
n=read();
rep(i,1,n) a[i]=read();
rep(i,1,n) b[i]=read();
rep(i,2,n) b[i]-=a[i];
rep(i,1,n) {p[i].a=1;p[i].b=0;nowp[i].a=1;nowp[i].b=0;}
rep(i,2,n)
{
p[i]=Poly(0,b[i])-nowp[i];
for (int j=i;j<=n;j+=i) nowp[j]=nowp[j]+p[i];
}
ll c=0;
rep(i,1,n)
{
if (p[i].a==0) c+=abs(p[i].b);
else
{
pp[++m]=p[i];
if (pp[m].a<0) {pp[m].a*=-1;pp[m].b*=-1;}
}
}
sort(pp+1,pp+1+m,cmp);
rep(i,1,m) pre[i]=pre[i-1]+pp[i];
per(i,m,1) suf[i]=suf[i+1]+pp[i];
int q=read();
while (q--)
{
int x=read(),l=1,r=m,pos=0;x-=a[1];
while (l<=r)
{
int mid=(l+r)>>1;
if (pp[mid].a*x+pp[mid].b<=0) {pos=mid;l=mid+1;}
else r=mid-1;
}
ll ans1=pre[pos].a*x+pre[pos].b,
ans2=suf[pos+1].a*x+suf[pos+1].b;
printf("%lld\n",ans2-ans1+c);
}
return 0;
}