AGC019


image

質量果然挺高的。

A

貪心。

ll Q,H,S,D,N;
int main()
{
    cin>>Q>>H>>S>>D>>N;
    H=min(H,Q+Q);
    S=min(S,H+H);
    D=min(D,S+S);
    ll ans=N/2*D+(N&1)*S;
    cout<<ans<<"\n";
}

B

認真目測樣例可以發現答案為不相等的字符對數+1。

char str[SZ]; int n;
int app[SZ];
int main()
{
    scanf("%s",str+1);
    n=strlen(str+1);
    for(int i=1;i<=n;++i)
        ++app[str[i]];
    ll tot=n*(ll)(n-1)/2;
    for(int i='a';i<='z';++i)
        tot-=(app[i]-1)*ll(app[i])/2;
    cout<<tot+1<<"\n";
}

C

首先答案大體上就是曼哈頓距離*100,為了幾個噴泉繞路顯然不優。

每一個image可以使答案變小20-5pi,每個image會使答案變大10pi-20。

為了讓image盡量多,我們會經過盡量多的噴泉,這個可以直接按行排序之后把列LIS。

什么時候會有image呢?只有正好每行或者每列都懟了一個噴泉的時候。判一下就好了。

#define y1 yyy1
#define y2 yyy2
int x1,y1,x2,y2,n;
pii ps[SZ];
ld pi=acos(-1);
int bs[SZ];
void upd(int x,int y)
{
    for(;x<=n;x+=x&-x) bs[x]=max(bs[x],y);
}
int qmax(int x)
{
    int ans=-1e9;
    for(;x>=1;x-=x&-x) ans=max(ans,bs[x]);
    return ans;
}
#define yn yyn
int ys[SZ],yn=0;
int lis[SZ];
int main()
{
    memset(bs,-127/3,sizeof bs);
    scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&n);
    for(int i=1;i<=n;++i)
        scanf("%d%d",&ps[i].fi,&ps[i].se);
    if(x1>x2)
    {
        x1*=-1; x2*=-1;
        for(int i=1;i<=n;++i)
            ps[i].fi*=-1;
    }
    if(y1>y2)
    {
        y1*=-1; y2*=-1;
        for(int i=1;i<=n;++i)
            ps[i].se*=-1;
    }
    ps[++n]=pii(x1,y1);
    ps[++n]=pii(x2,y2);
    sort(ps+1,ps+1+n);
    for(int i=1;i<=n;++i)
        ys[++yn]=ps[i].se;
    sort(ys+1,ys+1+yn);
    int yy1=lower_bound(ys+1,ys+1+yn,y1)-ys;
    int yy2=lower_bound(ys+1,ys+1+yn,y2)-ys;
    for(int i=1;i<=n;++i)
        ps[i].se=lower_bound(ys+1,ys+1+yn,ps[i].se)-ys;
    int rr=0;
    for(int i=1;i<=n;++i)
    {
        lis[i]=qmax(ps[i].se)+1;
        if(ps[i]==pii(x1,yy1)) lis[i]=0;
        else if(ps[i]==pii(x2,yy2)) rr=lis[i]-1;
        upd(ps[i].se,lis[i]);
    }
    ld ans=(x2-x1)+(y2-y1);
    ans*=100.0;
    ans+=(pi-4)*5*rr;
    if(rr==min(x2-x1+1,y2-y1+1)&&rr)
        ans+=pi*5;
    printf("%.20lf\n",ans);
}

D

為了方便描述,我們記一個計數器t,往右+1,往左-1。那么當前a[i]對着的就是b[i+t](模|A|意義下)。

假設我們枚舉最后相等的時候的t=s,那么a[i]對着b[i+s],我們就知道了哪些位需要flip一下。

考慮A在轉的時候設計數器最小值為minn,最大值為maxn(minn<=0<=maxn)。要翻轉A的某一位,可以往左轉到第一個B為1的位置,也可以往右轉到第一個B為1的位置,即有一個條件minn<=某個值 或者 maxn>=某個值。

要取到某個minn和maxn、最后到達s,需要的最小翻的次數是多少呢?可以發現恰好是2(maxn-minn)-|s|。這個公式可以這么理解:如果s=0,那么顯然要2(maxn-minn),先移到maxn,再移到minn,再移回來或者先到minn再到maxn再回來。如果s>0,那么先到minn,再到maxn,然后只要移回到s,所以就是2(maxn-minn)-s。

假設s>=0,那么需要次數就是2(maxn-minn)-s,並且maxn>=s,minn<=0。設x=-minn,y=maxn-s,那么我們就是要最小化2(y+s+x)-s=2(x+y)+s,即最小化x+y,原來的條件就是x>=某個數 or y>=某個數。掃一下就好了。(從大到小枚舉x,對不滿足x的y需求取max)

如果s<0那就把A和B全翻過來。

char A[SZ],B[SZ]; int n,ans;
int mi[SZ],ma[SZ],mx[SZ];
void work()
{
    for(int i=0;i<n;++i)
    {
        mi[i]=ma[i]=0;
        while(B[(i+ma[i])%n]!='1') ++ma[i];
        while(B[(i+mi[i]+n)%n]!='1') --mi[i];
    }
    for(int s=0;s<n;++s)
    {
        for(int r=0;r<n;++r) mx[r]=0;
        int dif=0;
        for(int r=0;r<n;++r) if(A[r]!=B[(r+s)%n])
            mx[-mi[r]]=max(mx[-mi[r]],ma[r]-s),++dif;
        int ly=0,ca=1e9;
        for(int r=n-1;r>=0;--r)
            ca=min(ca,ly+r), ly=max(ly,mx[r]);
        ans=min(ans,2*ca+s+dif);
    }
}
int main()
{
    scanf("%s%s",A,B);
    n=strlen(A); ans=1e9;
    bool ha=0,hb=0;
    for(int i=0;i<n;++i)
        if(A[i]=='1')ha=1;
    for(int i=0;i<n;++i)
        if(B[i]=='1')hb=1;
    if(!hb)
    {
        if(ha) puts("-1");
        else puts("0");
        return 0;
    }
    work(); reverse(A,A+n);
    reverse(B,B+n); work();
    printf("%d\n",ans);
}

E

這里講一下 http://codeforces.com/blog/entry/54027?#comment-381549 這個做法。

考慮swap有幾種:

A 10  10  11  11

B 01  11  11  01

其中

11

01

交換完之后第一列的10就固定了,直接就GG了。

首先我們可以注意到01和10的列數是一樣的(否則A和B里1的個數就不同了)。

我們先考慮11 11這種情況,貢獻A的一方只能是11,因為如果是01換過來一個1,那么A已經換過了,貢獻B的一方可以是換過來一個1的01(而且01合法的只能是換過來一個1)(注意這里貢獻A和B的可以是同一個)。

因為11 11換了也是白換,所以我們可以先把這部分的方案數計算一下。

假設有x個列為01,y個列為11,設有y-i個11 11這種情況,那么貢獻A的可以是y個中任一,貢獻B的可以是x+y中任一,所以只要把另外兩種情況的方案數乘上就好。

接下來我們只有

10 和 10 這兩種情況了。

01     11

此時,設g[x][i]為有x個列為01,x個列為10,i個列為11的方案數,那么g[x][i]=x^2g[x-1][i](10和01匹配)+xig[x][i-1](11和01匹配)

通過換元這個式子還可以進一步化簡,不是重點這里就不說了,可以參看上面那個鏈接。

char A[SZ],B[SZ];
int n;
ll f[10009];
#define MOD 998244353
ll fac[SZ],rfac[SZ];
ll qp(ll a,ll b)
{
    ll x=1;
    while(b)
    {
        if(b&1) x=x*a%MOD;
        a=a*a%MOD; b>>=1;
    }
    return x;
}
int main()
{
    fac[0]=1;
    for(int i=1;i<SZ;++i)
        fac[i]=fac[i-1]*i%MOD;
    rfac[SZ-1]=qp(fac[SZ-1],MOD-2);
    for(int i=SZ-1;i>=1;--i)
        rfac[i-1]=rfac[i]*i%MOD;
    scanf("%s%s",A+1,B+1); n=strlen(A+1);
    int x=0,y=0;
    for(int i=1;i<=n;++i) if(A[i]=='1')
        (B[i]=='0')?(++x):(++y);
    f[0]=1;
    for(int i=1;i<=x;++i)
        for(int j=1;j<=y;++j)
            f[j]=(f[j]+i*f[j-1])%MOD;
    ll ans=0;
    for(int j=0;j<=y;++j)
        ans=(ans+f[j]*rfac[x+j])%MOD;
    ans=ans*fac[x]%MOD*fac[x]%MOD
    *fac[y]%MOD*fac[x+y]%MOD;
    ans=(ans%MOD+MOD)%MOD;
    printf("%d\n",int(ans));
}

多項式做法可以參見 http://blog.csdn.net/wxh010910/article/details/77622473

F

因為每次回答后都告訴了你正誤,所以最優策略一定是答剩的比較多的一個,答對概率就是 剩的多的個數 / 總個數。

image

考慮把回答的過程畫成圖中這樣,每個點上標上之前說的 剩的多的個數/總個數,那么問的就是這個網格圖中往右往下走從S到T的期望路徑長度。

考慮把分子不一樣的格線標成實線,那么圖一定形如這樣

image

主要的思路是從T開始一條一條對角線往上枚舉。一條路徑顯然恰過每條對角線的一個點。

考慮一條路徑在當前這條對角線的交點的分子,如果我們知道了每個分子的和,那么ans+=分子/公分母/路徑數 即可。

如下圖考慮一條分子為5433的對角線,上一條對角線分子為4323。

image

考慮一條路徑經過上一條對角線之后經過這一條對角線,分子只能+1或者不變,如果經過實線的話就會+1,否則不變。

那么現在我們的任務就變成了統計經過這些實線的路徑條數。

如上圖,我們先考慮橫着的實線,計數線不太好,我們考慮把格子往左壓扁一格:

image

顯然原來經過實線對應現在經過綠點。

這個計數仍然不好計,考慮還是利用遞推,假設我們知道了上一條對角線經過這些點的方案數如何推出下一條的?

有兩種基本情況:

image

假設我們知道了經過橙點的方案數。

這種情況下考慮經過紫點的路徑一定也經過橙點,而經過橙點的路徑,不經過紫點的只有經過最右邊那個橙點,並且沒經過最右邊的紫點的路徑,方案數即從(S到這個橙點路徑數-S到最右邊紫點路徑數)*這個橙點到T的路徑數。

image

這種情況類似,經過紫點而不經過橙點的路徑數只有 S到最右邊紫點路徑數*(最右邊紫點到T路徑數-最右邊橙點到T路徑數)。

需要注意的是實際上是哪種情況並不是由點數決定的,而是由最右邊的紫點和橙點的位置關系決定的。

如何方便地求出某兩條對角線間最右邊的一段實線呢?如圖(瞎畫的,不要在意細節):

image

把對角線和灰線求交點,上/下取整即為最右邊的實線位置。

#define SZ 1234567
int n,m;
const int MOD=998244353;
ll fac[SZ],rfac[SZ];
ll qp(ll a,ll b)
{
    ll x=1;
    while(b)
    {
        if(b&1) x=x*a%MOD;
        a=a*a%MOD; b>>=1;
    }
    return x;
}
inline ll rt(int a,int b)
{
    return fac[a+b]*rfac[a]%MOD*rfac[b]%MOD;
}
int main()
{
    fac[0]=1;
    for(int i=1;i<SZ;++i) fac[i]=fac[i-1]*i%MOD;
    rfac[SZ-1]=qp(fac[SZ-1],MOD-2);
    for(int i=SZ-1;i>=1;--i)
        rfac[i-1]=rfac[i]*i%MOD;
    cin>>n>>m;
    if(n>m) swap(n,m);
    //灰線為y=x+m-n
    int px1,py1,px2,py2; ll cv1,cv2,cv=0,ans=0;
    //考慮對角線x+y=g和x+y=g+1的關系 
    for(int g=m+n-1;g>=0;--g)
    {
        {
        int r=max(g+n-m+1,0),y,u=min(g,n);
        if(r&1) y=(r+1)/2;
        else y=r/2;
        int a=y,b=g-y;
        if(y==u) cv1=rt(y,g-y)*rt(n-y,m-1-(g-y))%MOD;
        else if(px1==a)
            cv1-=(rt(px1,py1)-rt(a,b))*rt(n-px1,m-1-py1)%MOD;
        else
            cv1+=rt(a,b)*(rt(n-a,m-1-b)-rt(n-px1,m-1-py1))%MOD;
        px1=a; py1=b; cv1%=MOD;
        }
        {
        int r=g+n-m+1,y,d=max(g-m,0);
        if(r&1) y=(r-1)/2;
        else y=r/2; --y;
        if(d>y) cv2=0;
        else
        {
        int a=y,b=g-y;
        if(y==d) cv2=rt(y,g-y)*rt(n-1-y,m-(g-y))%MOD;
        else if(px2!=a)
            cv2-=(rt(px2,py2)-rt(a,b))*rt(n-1-px2,m-py2)%MOD;
        else
            cv2+=rt(a,b)*(rt(n-1-a,m-b)-rt(n-1-px2,m-py2))%MOD;
        px2=a; py2=b; cv2%=MOD;
        }
        }
        cv+=cv1+cv2; cv%=MOD;
        ans+=cv*qp(m+n-g,MOD-2)%MOD;
    }
    ans=ans%MOD*qp(rt(n,m),MOD-2)%MOD;
    ans=(ans%MOD+MOD)%MOD;
    printf("%d\n",int(ans));
}

W`~U0Q0$Z2ALXG(T1GEX{)RUPD:wxh吊打tourist http://blog.csdn.net/wxh010910/article/details/77752687

考慮答案變得不那么套路的時候,就是經過灰線的時候,直接算就好了W`~U0Q0$Z2ALXG(T1GEX{)R


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM