1. 程式人生 > 其它 >Codeforces Round #754 (Div. 2) 部分題解

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

通過手玩可以發現:最小的合法串不會很長(最長的串為abbaccaaccabba),所以直接列舉所有長度\(\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;
}