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]\)
由於樹是一張二分圖,我們可以將其黑白染色。對點數較少(\(\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;
}