[Codeforces]#179 div1-----295ABCDE

摘自我的github:https://github.com/Anoxxx

The Solution

Source: Codeforces Round #179 (Div. 1)
    VJudge链接:  https://cn.vjudge.net/contest/167920#problem
CodeForces链接:  http://codeforces.com/problemset/problem/295

#A Greg and Array

Time limit: 1500 ms
Memory limit: 262144 kB
Tags: 差分,线段树

题意

给你n个数,m个操作,每个操作包含l、r、d,表示区间[l,r]+d,再给你k个总操作,每个总操作包含x、y,表示做第x到第y个操作,问最后各个数的数值;

题解

两次打标记(就是两次差分

#include<cstdio>
#include<algorithm>
using namespace std;
long long a[100100],l[100100],p,t[100100];
int n,m,k,x,y;
struct node{
    int l,r,d;
}c[100100];
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=0;i<n;i++)scanf("%lld",&a[i]);
    for(int i=0;i<m;i++)scanf("%d%d%d",&c[i].l,&c[i].r,&c[i].d);
    for(int i=0;i<k;i++){
        scanf("%d%d",&x,&y);
        t[x-1]++;t[y]--;
    }
    for(int i=0;i<m;i++){
        p+=t[i];
        l[c[i].l-1]+=p*c[i].d;l[c[i].r]-=p*c[i].d;
    }
    p=0;
    for(int i=0;i<n;i++){
        p+=l[i];
        a[i]+=p;
        printf("%lld ",a[i]);
    }
}

#B Greg and Graph

Time limit: 3000 ms
Memory limit: 262144 kB
Tags: 最短路,逆向思维

题意

给你n个点,每两个不同的点间路的距离,n次操作,每次操作删除一个点,求每次操作前所有点对之间的最短路之和。

题解

可以把每次操作删除一个点,看做从后面的操作到前面的操作依次加点,然后对于每次加点对其他所有点进行松弛; 然后每次的答案即为已加入各点目前为止间的最短路之和,倒着输出即可;

#include<cstdio>
#include<algorithm>
using namespace std;
int n,b[501],x[501];
long long ans[501],a[501][501],f[501][501];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++){
        scanf("%lld",&a[i][j]);f[i][j]=a[i][j];
    }
    for(int i=1;i<=n;i++)scanf("%d",&x[i]);
    for(int i=n;i>=1;i--){
        b[x[i]]=1;
        for(int j=1;j<=n;j++)
        for(int k=1;k<=n;k++)f[j][k]=min(f[j][k],f[j][x[i]]+f[x[i]][k]);
        for(int j=1;j<=n;j++)
        for(int k=1;k<=n;k++)if(b[j]&&b[k])ans[i]+=f[j][k];

    }
    for(int i=1;i<=n;i++)printf("%lld ",ans[i]);
    return 0;
}

#C Greg and Friends

Time limit: 2000 ms
Memory limit: 262144 kB
Tags: dp,bfs

题意

有n个人在岸边,每个人不是50kg就是100kg,有一艘船,载重量为k,可以载人过河,但每次必须有人划船,问使所有人都到对岸的最少过河次数和这个次数的方案数。

题解

最少过河次数bfs无脑求就行(好像都不用bfs……,设f[i][j][k]表示有i个50kg的人和j个100kg的人在原岸,船在(k==1?原岸:对面)的最少次数的方案数, 显然这可以在bfs的时候通过组合数维护,注意bfs的判重即可;

#include<cstdio>
#include<algorithm>
#define MOD 1000000007
using namespace std;
int b[51][51][2][51][51],q[500000][4],anx,aum,num5,num10,x,n,k,h,t;
long long f[51][51][2],c[100][100];
void push(int px,int py,int pz,int i,int j,int val){
    int x=px+i,y=py+j,z=pz^1;i=abs(i),j=abs(j);
    if(anx&&val>anx)return;
    if(b[px][py][pz][x][y])return;

    if(pz==0)f[x][y][z]=(f[x][y][z]+(f[px][py][pz]*(c[num5-px][i]*c[num10-py][j])%MOD)%MOD)%MOD;
    else f[x][y][z]=(f[x][y][z]+(f[px][py][pz]*(c[px][i]*c[py][j])%MOD)%MOD)%MOD;
    q[++t][0]=x;q[t][1]=y;q[t][2]=z;q[t][3]=val;b[px][py][pz][x][y]=1;
    if(x==num5&&y==num10&&z==1&&!anx)anx=val;
}
void getC(){
    c[0][0]=1;
    for(int i=1;i<=90;i++)
    for(int j=0;j<=i;j++)c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;
}
int main(){
    getC();
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        x==50?num5++:num10++;
    }
    f[0][0][0]=1;t++;
    q[t][0]=q[t][1]=q[t][2]=q[t][3]=0;b[0][0][0][0][0]=1;
    while(h<t){
        int x=q[++h][0],y=q[h][1],z=q[h][2],val=q[h][3];
        if(z==0){
            int xp=num5-x,yp=num10-y;
            for(int i=1;i<=min(yp,k/100);i++)push(x,y,z,0,i,val+1);
            for(int i=1;i<=min(xp,k/50);i++)
            for(int j=0;j<=min(yp,(k-i*50)/100);j++)
            push(x,y,z,i,j,val+1);
        }
        else{
            for(int i=1;i<=min(y,k/100);i++)push(x,y,z,0,-i,val+1);
            for(int i=1;i<=min(x,k/50);i++)
            for(int j=0;j<=min(y,(k-i*50)/100);j++)
            push(x,y,z,-i,-j,val+1);
        }
    }
    return anx==0?printf("%d\n%d",-1,0):printf("%d\n%d",anx,f[num5][num10][1]),0;
}

#D New Year Letter

Time limit: 2000 ms
Memory limit: 262144 kB
Tags: dp

题意

对于一个n行m列的黑白矩阵,存在cave当且仅当:

1、存在[l,r]使得第l行到第r行都有且仅有两个黑色格子,其他行没有;

2、存在一个行号t,使得:

1)对于任意存在黑色格子的行,设两个黑色格子的列号为x和y(x<y),则设集合s(r)=[x,y];

2)任意取t及t之上的两个有黑色格子行,令上方的行为u,下方的行为d,则s(u)属于s(d);

3)任意取t及t之下的两个有黑色格子行,令上方的行为u,下方的行为d,则s(d)属于s(u); 求n行m列的黑白矩阵存在cave的方案数;

题解

设f[i][j]表示前i行只让上面的行符合情况,第i行集合长度为j(可以看做两个黑色块一个在1,一个在j)的方案数,则:

1、f[i][j]=sigma(f[i-1]k)+f[i][j-1]; 前面的sigma是显然的,而f[i][j-1]是因为,f[i][j-1]的情况可以整体向右移一位并依然符合f[i][j]的定义;

2、ans=sigma((f[i][j]-f[i-1][j])f[n-i+1][j](m-j+1))(1<=i<=n,2<=j<=m); f[i][j]-f[i-1][j]是因为第i-1行长度为j且第i行长度为j的方案可能重复计算,需要避免 f[n-i+1][j]则是f[i][j]只算了上面符合情况的,我们需要和下面符合情况的方案数乘起来 m-j+1则是因为这个区间可以左右移啊

#include<cstdio>
#define MOD 1000000007
#define ll long long
using namespace std;
ll ans,n,m,s,f[2001][2001];
int main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++)f[1][i]=1;
    for(int i=2;i<=n;i++){
        int s=0;f[i][1]=1;
        for(int j=2;j<=m;j++){
            s=(s+f[i-1][j])%MOD;
            f[i][j]=(f[i][j-1]+s)%MOD;
        }
    }
    for(int i=1;i<=n;i++)
    for(int j=2;j<=m;j++)ans=(ans+(m-j+1)*(f[i][j]-f[i-1][j]+MOD)%MOD*f[n-i+1][j])%MOD;
    printf("%lld",ans);
}

#E Yaroslav and Points

 Time limit: 2000 ms
 Memory limit: 262144 kB
 Tags: 动态开点线段树

题意

给你n个x轴上的坐标,给你两个操作: 1、把输入的第j个点的坐标右移d 2、给定区间,输出区间内点之间的距离和

题解

开一个-1e8到1e8的线段树,对于每一个点,就把含有这个点坐标的区间更新,cnt代表区间内的点数,sum是区间内的点的坐标和,ans就是答案:

c.sum=a.sum+b.sum;

c.cnt=a.cnt+b.cnt;

c.ans=a.ans+b.ans+a.cntb.sum-b.cnta.sum;

(为什么这样??手动模拟合并两个区间你就知道啦 最后因为空间开不下那么多点,于是动态开点,要用到再标号,就可以啦

#include<cstdio>
#include<algorithm>
#define root -1000000001,1000000001,1
#define lson l,m
#define rson m+1,r
#define ll long long
using namespace std;
struct node{
    ll cnt,sum,ans;
    int ls,rs;
    node(){
        cnt=sum=ans=ls=rs=0;
    }
}a[6000000],zero;
int n,j,p,x[100010],type,m,l,r,tot=1;
node merge(node c,node a,node b){
    c.sum=a.sum+b.sum;
    c.cnt=a.cnt+b.cnt;
    c.ans=a.ans+b.ans+a.cnt*b.sum-b.cnt*a.sum;
    return c;
}
void update(int val,int z,int l,int r,int rt){
    if(val<l||val>r)return;
    if(l==r){
        a[rt].ans=0;a[rt].cnt+=z;a[rt].sum+=z*val;return;
    }
    if(a[rt].ls==0)a[rt].ls=++tot;
    if(a[rt].rs==0)a[rt].rs=++tot;
    int m=(l+r)>>1;
    if(m>=val)update(val,z,lson,a[rt].ls);
    if(m<val)update(val,z,rson,a[rt].rs);
    a[rt]=merge(a[rt],a[a[rt].ls],a[a[rt].rs]);
    //printf("%d %d %d %d %d\n",a[rt].ls,a[rt].rs,a[rt].ans,a[rt].cnt,a[rt].sum);
}
node query(int x,int y,int l,int r,int rt){
    if(y<l||x>r)return zero;
    if(x<=l&&r<=y)return a[rt];
    int m=(l+r)>>1;node p,q;
    if(m>=x)p=query(x,y,lson,a[rt].ls);
    if(m<y)q=query(x,y,rson,a[rt].rs);
    return merge(a[rt],p,q);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&x[i]);
        update(x[i],1,root);
        //printf("%d %d\n",a[1].rs,a[1].ls);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%d",&type);
        if(type==1){
            scanf("%d%d",&j,&p);
            update(x[j],-1,root);x[j]+=p;update(x[j],1,root);
        }
        else{
            scanf("%d%d",&l,&r);
            printf("%lld\n",query(l,r,root).ans);
        }
    }
}
时间: 2024-10-22 04:50:24

[Codeforces]#179 div1-----295ABCDE的相关文章

codeforces #305 div1 done

总算搞定了这一场比赛的题目,感觉收获蛮大 其中A,B,C都能通过自己的思考解决掉 D题思路好神,E题仔细想想也能想出来 以后坚持每两天或者一天做一场CF的div1的全套题目 除非有实在无法做出来的题目,每道题目还是都要写题解的 (我这算不算立flag? 本蒟蒻写的题解的链接: A:http://www.cnblogs.com/joyouth/p/5352953.html B:http://www.cnblogs.com/joyouth/p/5352932.html C:http://www.cn

codeforces #334 div1 603C Lieges of Legendre(博弈)

题目链接: codeforces 603C 题目大意: 有两个人做游戏,游戏规则如下: 有n堆石子,每次可以对一堆石子进行操作,如果当前石子是偶数,那么可以选择将这2*x个石子分成k堆石子数为x的石子堆,还有一种没有前提的操作是取走当前堆的一个石子,问先手赢还是后手赢,先手和后手都足够聪明的情况下. 题目分析: 首先对于这种组合游戏的题目,很容易想到利用SG函数来解.我们对于游戏的局势进行分类讨论: 当k是偶数的情况下, 我们可以知道如果要把一个偶数堆分成k个堆,相当于将局势转到一个新的组合游戏

Codeforces #594 div1 C/div2 E – Queue in the Train

题目链接:https://codeforces.com/contest/1239/problem/C 题意:火车上有n位乘客,按照1到n编号,编号为i的人会在ti分钟想去打水.水箱只能供一位乘客使用,每位乘客会使用p分钟.当一位乘客想要去打水时,他会先看编号在他前面的乘客是不是都在座位上,如果有人没在座位上,他会坐下继续等待,否则他会去排队打水.当某一时刻有几位乘客同时想要打水时,编号最小的乘客会前去打水,其他人会坐下继续等待,计算每位乘客打完水的时间. 做法:模拟.按照题意模拟即可,具体实现见

codeforces#426(div1) B - The Bakery (线段树 + dp)

题意:把 n 个数划分成 m 段,要求每组数不相等的数的数量最大之和. 思路: dp方程 : dp[i][j] = max( dp[k][j-1] + v(k, i) );( j<=k<i , k = j, j+1, +...+ i-1) dp[i][j]表示第 i 个数分到第 j 段的最大值. v(k, i) 表示k~i中不同数的个数,此处用hash记录每个数上一次出现的位置,从上一次出现的位置到当前位置的 dp[i][j-1] 值均可+1. 此时时间复杂度 O(n*m*log(n)). 线

codeforces #313 div1 D

好神的题目! 首先我们运用pick定理A=S-B/2+1将要求的东西转化掉 之后分离变量,我们变成了求选取凸包面积的期望和求选取凸包在边界上的点的期望 我们先考虑求选取凸包面积的期望 如何计算凸多边形的面积,我们可以原点为划分点,计算凸包上的每个向量的叉积的和 如何计算凸包边界上的点,我们可以计算凸包上的每个向量上的点 那么我们可以考虑每个向量被计算的概率 显然p(i)-p(i+k)这个向量被计算的概率为(2^(n-k-1)-1)/(2^n-1-n-n*(n-1)/2)次 这样我们就可以O(n^

codeforces #310 div1 E

算得上是比较水的E题了吧,自己想了想写了写居然1A了 对于这道题,我们很容易想到对于原图的一个边双,定向后任意两点间一定可达 那么我们可以求出原图的边双并将每个边双缩成一个点 那么原图就变成了无环的无向图,也就是一片森林 之后我们考虑每个任务: 1.如果S和T处于一个边双里,显然是可行的 2.如果S和T处于两棵树中,显然不连通不可行 3.S和T处于一棵树中,那么S->lca的所有边都是向上的,lca->T的所有边的都是向下的 对于第三种情况,我们可以在S上打一个up标记,在T上打一个down标

【codeforces #275(div1)】AB题解

A. Diverse Permutation time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Permutation p is an ordered set of integers p1,???p2,???...,???pn, consisting of n distinct positive integers not larg

codeforces #313 div1 C

同BZOJ 3782 上学路线 QAQ 还比那个简单一点 把坐标(1,1)-(n,m)平移成(0,0)-(n-1,m-1) 设dp[i]表示从(1,1)出发第一次经过障碍且到达第i个障碍的方案数 首先到达第i个障碍的方案数为C(x+y,x) 之后我们考虑i是第一个经过的障碍的方案数=到达i的方案数-i不是第一个经过的障碍的方案数 这也是很好算的 容斥一下即可 #include<cstdio> #include<cstring> #include<iostream> #i

codeforces #334 div1 B 603B Moodular Arithmetic(数论)

题目链接: codeforce 603B 题目大意: 给出f(kx mod p)≡kf(x) mod p,求满足条件的f(x)的数量. 题目分析: 首先考虑两种特殊情况,即k=0和k=1的情况. 当k = 0 时, {f(x)=0f(x)={0,?p?1},x=0,x>0 因为k=0?f(kx mod p)≡f(0),x<p 所以只有f(0)必须等于0,其他的f(x)可以为任意值域中的值,所以方案数是pp?1 当k=1时, f(x mod p)=f(x) mod p,x<p?x mod