Codeforces乱刷集合

发这篇博客的目的是因为刷了些水题,但又有一些不错的地方可以加以借鉴....然后又不想一个一个发....

 

 

Codeforces731A

题目大意:给出一个26个字母的环,初始指向a,可以顺时针转或者逆时针转,给出一个字符串求最少转几次能得到他。

题解:傻逼模拟。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
char S[100000];
long long ans;
int main()
{
    scanf("%s",S+1); int len=strlen(S+1);
    int now,last,a; last='a';
    for (int i=1; i<=len; i++)
        {
            now=S[i];
            if (now>last) a=now-last; else a=last-now;
            ans+=min(a,26-a);
            last=now;
        }
    printf("%lld\n",ans);
    return 0;
}
731A

 

Codeforces731B

题目大意:给出一个序列,你可以进行两种操作,1.一个位置-2;2.相邻两个位置-1;问是否可以恰好使所有数减到0。

题解:贪心,因为最左边的相邻的只有它右边一个,所以可以从最左边的开始搞,注意不会减到0即可。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define N  200005
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int n,a[N],f[N];
int main()
{
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    a[1]%=2;
    for(int i=1;i<=n;i++){
        a[i]-=a[i-1];
        if(a[i] < 0) { puts("NO"); return 0; }
        a[i]%=2;
    }
    if(a[n]) puts("NO"); else puts("YES");
    return 0;
}
731B

 

Codeforces731C

题目大意:给出一个序列,每个位置有一个颜色,再给出一些区间要求这些区间中的颜色必须全都一样,求最少需要改变这个序列中的多少个位置,能使所有区间满足。

题解:可以用并查集搞一下,或者用连通分支搞一下。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int N,M,K,size,maxx,ans;
struct data{int l,r,f;}a[200010];
bool cmp(data x,data y) {return x.l==y.l? x.r<y.r : x.l<y.l;}
struct EdgeNode{int next,to;}edge[400010];
int head[200010],cnt=1;
int st[200010],top;
inline void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
inline void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);}
bool visit[200010];
int num[200010],c[200010];
void work(int x)
{
    /*printf("%d\n",x);*/
    visit[x]=1; size++; num[c[x]]++; st[++top]=x;
    if (num[c[x]]>maxx) maxx=num[c[x]];
    for (int i=head[x]; i; i=edge[i].next) if (!visit[edge[i].to]) work(edge[i].to);
}
void clear()
{
    for (int i=1; i<=top; i++) num[c[st[i]]]--; top=0;
}
int main()
{
    N=read(),M=read(),K=read();
    for (int i=1; i<=N; i++) c[i]=read();
    for (int i=1; i<=M; i++) a[i].l=read(),a[i].r=read(),InsertEdge(a[i].l,a[i].r);
    for (int i=1; i<=N; i++)
        if (!visit[i]) maxx=0,size=0,work(i),ans+=size-maxx,clear();/*,printf("a  %d\n",i)*/
    printf("%d\n",ans);    
    return 0;
}
731C

 

Codeforces731D

题目大意:给出许多个序列,每次操作可以使这些数列中所有数+1,当一个数超过C后会还原成0,求最小需要几次操作后,满足给出的这些序列的按读入顺序且按字典序单调增,不行则输出-1。

题解:考虑每个序列和它前一个数列比较,如果能够比它前一个数列递增,能够得到一段满足的操作数的区间,然后对所有区间求交算答案即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int N,C,a[2][500010],len[500010],x;
struct data{int l,r;}seg[1000010]; int cnt;
inline bool cmp(data A,data B) {return A.l==B.l? A.r<B.r : A.l<B.l;}
int main()
{
    N=read(),C=read();
    x=0;
    for (int i=1; i<=N; i++,x^=1)
        {
            len[i]=read(); for (int j=1; j<=len[i]; j++) a[x][j]=read();
            if (i!=1)
                {
                    int le=min(len[i],len[i-1]),pos=0;
                    for (int j=1; j<=le; j++) 
                        if (a[x][j]==a[x^1][j]) continue; else {pos=j; break;}
                    if (!pos && len[i]<len[i-1]) {puts("-1"); return 0;}
                    if (pos) 
                        if (a[x][pos]>a[x^1][pos]) 
                            seg[++cnt]=(data){C+1-a[x][pos],C-a[x^1][pos]};
                        else 
                            seg[++cnt]=(data){0,C-a[x^1][pos]},seg[++cnt]=(data){C+1-a[x][pos],C+1};
                }
        }
    for (int i=1; i<=cnt; i++) if (seg[i].l>seg[i].r) swap(seg[i].l,seg[i].r);
//    for (int i=1; i<=cnt; i++) printf("%d %d\n",seg[i].l,seg[i].r);
    sort(seg+1,seg+cnt+1,cmp);
    int pos=0;
    for (int i=1; i<=cnt; i++)
        if (pos<seg[i].l) break;
            else pos=max(pos,seg[i].r+1);
    printf("%d\n",(pos<=C)? pos:-1);
    return 0;
}
731D

 

Codeforces731E

题目大意:给出一个游戏,首先由一个数列,每次可以从左边开始选择一段数(>2个数)合并,并得到这段的分数,只剩一个数时结束,问双方都执行最优策略的时候,游戏结束时A最多比B多多少分。

题解:比较容易的dp,可以用前缀和优化,记录一下后缀最大值。dp的方程是$dp[i]=max(dp[i],sum[j]-dp[j])$,然而dp的值完全不用记录,可以直接扫一遍。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
int N,a[200010],s[200010],ans;
int main()
{
    scanf("%d",&N); for (int i=1; i<=N; i++) scanf("%d",&a[i]),s[i]=s[i-1]+a[i];
    ans=s[N]; for (int i=N-1; i>1; i--) ans=max(ans,s[i]-ans); printf("%d\n",ans);
    return 0;
}
731E

 

Codeforces731F

题目大意:给一个序列,问从序列中找一个数,把不小于它的数变成它或者它的倍数,并使得这些数的和最大

题解:可以直接处理前缀和后暴力更新答案,之所以暴力的复杂度非常科学,因为这样是$\sum \frac{n}{i}$是调和级数,而它是发散的,所以复杂度并不大。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long
int N,a[1000010],n[1000010],sn[1000010],maxx;
LL ans,sum;
int main()
{
    scanf("%d",&N);
    for (int i=1; i<=N; i++) scanf("%d",&a[i]),n[a[i]]++,maxx=max(maxx,a[i]);
    for (int i=maxx; i; i--) sn[i]=sn[i+1]+n[i];
    for (int i=1,j=i; i<=maxx; i++,j=i)
        for (ans=max(ans,sum),sum=0; n[i] && j<=maxx; j+=i)
            sum+=(LL)j*(sn[j]-sn[j+i]);
    ans=max(ans,sum);
    printf("%I64d\n",ans);
    return 0;
}
731F

 

Codeforces148E

题目大意:有N行数,每次可以从某行的开头或结尾去掉一个数,并得到这个数的价值,问最多取M次的最大价值和是多少。

题解:背包DP,先一遍DP预处理出$f[i][j]$表示第i行取j个数的最大价值和,再利用$f[i][j]$进行DP求出$g[i][j]$表示前i行取j个数的最大价值和。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define size(i) s[i].size()-1
vector<int>s[110],sum[110];
int f[110][10010],g[110][10010],N,M;
int main()
{
    for (int i=1; i<=100; i++) s[i].push_back(0),sum[i].push_back(0);
    N=read(),M=read();
    for (int x,i=1,len,j; i<=N; i++) 
        for (len=read(),j=1; j<=len; j++)
            x=read(),s[i].push_back(x),sum[i].push_back(sum[i][j-1]+s[i][j]);
    for (int i=1; i<=N; i++)
        for (int j=1; j<=size(i); j++)
            for (int k=1; k+size(i)-j-1<=size(i); k++)
                f[i][j]=max(f[i][j],sum[i][size(i)]-(sum[i][k+size(i)-j-1]-sum[i][k-1]));
//    for (int i=1; i<=N; i++,puts(""))
//        for (int j=1; j<=size(i); j++)
//            printf("%d  ",f[i][j]);
    for (int i=1,j,len=0; i<=N; i++)
        for (len+=size(i),j=1; j<=len; j++)
            for (int k=0; k<=size(i); k++)
                if (j>=k && len-(size(i))>=j-k)
                    g[i][j]=max(g[i][j],g[i-1][j-k]+f[i][k]);
//    for (int i=1; i<=N; i++,puts(""))
//        for (int j=1; j<=size(i); j++)
//            printf("%d  ",g[i][j]);
    printf("%d\n",g[N][M]);
    return 0;
}
148E

 

Codeforces285E

题目大意:求出满足good position的个数恰好为K个的全排列的个数  good position定义是$|a_{i}-i|=1$

题解:这类题显然可以DP+容斥。$dp[i][j][k][l]$表示前i个已经放了j个gp,k表示i是否被放过,l表示i+1是否被放过,这样就可以转移。 最后统计答案,注意$ans[j]$出现在$ans[i]$中的次数是$C[j][i]$次。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define LL long long
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int N,K;
LL ans[1010],f[1010][1010][2][2],fac[1010],C[1010][1010];
#define P 1000000007
int main()
{
    fac[0]=1;     for (int i=1; i<=1000; i++) fac[i]=fac[i-1]*i%P;
    N=read(),K=read();
    C[0][0]=1; for (int j,i=1; i<=N; i++) for (C[i][0]=1,j=1; j<=i; j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%P;
    f[0][0][1][0]=1;
    for (int i=1; i<=N; i++)
        for (int j=0; j<=N-1; j++)
            for (int k=0; k<=1; k++)
                for (int l=0; l<=1; l++)
                    (f[i][j+1][l][0]+=(!k)? f[i-1][j][k][l]:0)%=P,
                    (f[i][j+1][l][1]+=(i!=N)? f[i-1][j][k][l]:0)%=P,
                    (f[i][j][l][0]+=f[i-1][j][k][l])%=P;
    for (int i=0; i<=N; i++)
        for (int j=0; j<=1; j++)
            for (int k=0; k<=1; k++)
                (ans[i]+=f[N][i][j][k])%=P;
    for (int i=0; i<=N; i++) (ans[i]*=fac[N-i])%=P;
    for (int i=N; i>=0; i--)
        for (int j=i+1; j<=N; j++) ans[i]=(ans[i]-ans[j]*C[j][i])%P,ans[i]+=(ans[i]<0)? P:0;
    printf("%I64d\n",ans[K]);    
    return 0;
}
285E

 

Codeforces148D

题目大意:一个袋子中一共有w个白球,b个黑球,公主和龙依次取一个球,取到率先白球的人获胜,每次龙取球,总会额外随机掉出一个球,求公主获胜的概率。

题解:显然概率DP,而且很好转移,$dp[i][j]$表示现在还剩i个白球,j个黑球的时公主先手的获胜概率,那么分情况讨论即可,转移可以看代码。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
double f[1010][1010];
int W,B;
int main()
{
    for (int i=1; i<=1000; i++) f[i][0]=1,f[0][i]=0;
    for (int i=1; i<=1000; i++)
        for (int j=1; j<=1000; j++)
                f[i][j]+=(double)i/(i+j),
                f[i][j]+=(j>=3)? ((double)j/(i+j))*((double)(j-1)/(i+j-1))*((double)(j-2)/(i+j-2))*((double)f[i][j-3]) : 0.0,
                f[i][j]+=(j>=2 && i>=1)? ((double)j/(i+j))*((double)(j-1)/(i+j-1))*((double)i/(i-1+j-1))*((double)f[i-1][j-2]) : 0.0;
    while (scanf("%d%d",&W,&B)!=EOF) printf("%.9lf\n",f[W][B]);
    return 0;
}
148D

 

Codeforces268D

题目大意:一共有N层,每层有4个方向可以爬,一次最多爬高度H,从底部爬到至少N-H+1~N的高度的方案数。

题解:自己只会$O(NH^4)$的方法,就是$dp[i][n][e][s][w]$表示当前在第i层,北东南西四个方向分别距离为n/e/s/w的方案数,这样还需要滚动掉第i维。

正解也是如此,不过有很巧妙的优化,因为每一层的操作总会存在一个距离为0的转移,所以就可以压掉一维状态,这样复杂度就是$O(NH^{3})$,这种优化或者可以理解为把这些状态旋转的记录下来,可以类比旋梯。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long
#define P 1000000009
int N,H;
LL f[2][31][31][31][31],ans;
#define dpn(now,nor,eas,sou,wes) f[now&1][nor<H? 0:H][eas+(eas<H)][sou+(sou<H)][wes+(wes<H)]
#define dpe(now,nor,eas,sou,wes) f[now&1][nor+(nor<H)][eas<H? 0:H][sou+(sou<H)][wes+(wes<H)]
#define dps(now,nor,eas,sou,wes) f[now&1][nor+(nor<H)][eas+(eas<H)][sou<H? 0:H][wes+(wes<H)]
#define dpw(now,nor,eas,sou,wes) f[now&1][nor+(nor<H)][eas+(eas<H)][sou+(sou<H)][wes<H? 0:H]
int main()
{
    scanf("%d%d",&N,&H);
    f[0][0][0][0][0]=1;
    for (int i=1; i<=N; i++,memset(f[i&1],0,sizeof(f[i&1])))
        for (int nor=0; nor<=H; nor++)
            for (int eas=0; eas<=H; eas++)
                for (int sou=0; sou<=H; sou++)
                    for (int wes=0; wes<=H; wes++)
                        (dpn(i,nor,eas,sou,wes)+=f[(i-1)&1][nor][eas][sou][wes])%=P,
                        (dpe(i,nor,eas,sou,wes)+=f[(i-1)&1][nor][eas][sou][wes])%=P,
                        (dps(i,nor,eas,sou,wes)+=f[(i-1)&1][nor][eas][sou][wes])%=P,
                        (dpw(i,nor,eas,sou,wes)+=f[(i-1)&1][nor][eas][sou][wes])%=P;
    for (int nor=0; nor<=H; nor++)
        for (int eas=0; eas<=H; eas++)
            for (int sou=0; sou<=H; sou++)
                for (int wes=0; wes<=H; wes++)
                    (ans+=f[N&1][nor][eas][sou][wes])%=P;
    ans=(ans-f[N&1][H][H][H][H]+P)%P;    
    printf("%I64d\n",ans);
    return 0;
}
268D未优化
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long
#define P 1000000009
int N,H;
LL f[2][2][31][31][31],ans;
int main()
{
    scanf("%d%d",&N,&H);
    f[0][0][0][0][0]=1;
    for (int i=1; i<=N; i++,memset(f[i&1],0,sizeof(f[i&1])))
        for (int a=0; a<=1; a++)
            for (int b=0; b<=H; b++)
                for (int c=0; c<=H; c++)
                    for (int d=0; d<=H; d++)
                        (f[i&1][!(a<1)][b+(b<H)][c+(c<H)][d+(d<H)]+=f[(i-1)&1][a][b][c][d])%=P,
                        (f[i&1][!(b<H)][c+(c<H)][d+(d<H)][a<1? 1:H]+=f[(i-1)&1][a][b][c][d])%=P,
                        (f[i&1][!(c<H)][d+(d<H)][a<1? 1:H][b+(b<H)]+=f[(i-1)&1][a][b][c][d])%=P,
                        (f[i&1][!(d<H)][a<1? 1:H][b+(b<H)][c+(c<H)]+=f[(i-1)&1][a][b][c][d])%=P;
    for (int a=0; a<=1; a++)
        for (int b=0; b<=H; b++)
            for (int c=0; c<=H; c++)
                for (int d=0; d<=H; d++)
                    (ans+=f[N&1][a][b][c][d])%=P;
    ans=(ans-f[N&1][1][H][H][H]+P)%P;
    printf("%I64d\n",ans);
    return 0;
}
268D优化

 

Codeforces697D

题目大意:模拟求树的DFS序的过程,要求求出每个节点的DFS序的期望,以1号为根。

题解:树形DP,一个节点的DFS序编号为它前面的被遍历到的点的数目,所以可以考虑统计出每个节点的子树size,然后可以得到dp方程$f[v]=f[u]+1+\frac{size[u]-size[v]-1}{2}$,然后就可以得到了。这种题必须要手画数据才能得到!!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define MAXN 100010
int N;
struct EdgeNode{int next,to;}edge[MAXN<<1];
int head[MAXN],cnt=1,size[MAXN];
inline void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
inline void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);}
double f[MAXN];
void DFS_1(int now,int last)
{
    size[now]=1;
    for (int i=head[now]; i; i=edge[i].next)
        if (edge[i].to!=last)
            DFS_1(edge[i].to,now),size[now]+=size[edge[i].to];
}
void DFS_2(int now,int last)
{
    for (int i=head[now]; i; i=edge[i].next)
        if (edge[i].to!=last)
            f[edge[i].to]=f[now]+1+(double)(size[now]-size[edge[i].to]-1)/2,DFS_2(edge[i].to,now);
}
int main()
{
    N=read();
    for (int x,i=2; i<=N; i++) x=read(),InsertEdge(x,i);
    DFS_1(1,0); f[1]=1; DFS_2(1,0);
    for (int i=1; i<=N; i++) printf("%.1lf ",f[i]);
    return 0;
} 
697D

 

posted @ 2016-10-18 21:42  DaD3zZ  阅读(328)  评论(0编辑  收藏  举报