洛谷 2017.7月赛解题报告

https://www.luogu.org/contest/show?tid=2437

 

期望得分:100+30+100+100=330

实际得分:100+30+0+20=150

T2 写了个DP,大佬说DP有后效性。。。

T3 输出时忘了把I64d换成lld, ——(仍然使用Windows系统的悲哀)

T4 莫名WA,原因不明

 

T1 P3817 小A的糖果

题目描述

小A有N个糖果盒,第i个盒中有a[i]颗糖果。

小A每次可以从其中一盒糖果中吃掉一颗,他想知道,要让任意两个相邻的盒子中加起来都只有x颗或以下的糖果,至少得吃掉几颗糖。

输入输出格式

输入格式:

 

第一行输入N和x。

第二行N个整数,为a[i]。

 

输出格式:

 

至少要吃掉的糖果数量。

 

输入输出样例

输入样例#13 3
2 2 2
输出样例#11

输入样例#26 1
1 6 1 2 0 4
输出样例#211
输入样例#35 9
3 1 4 1 5
输出样例#30
说明

样例解释1

吃掉第二盒中的糖果。

样例解释2

第二盒吃掉6颗,第四盒吃掉2颗,第六盒吃掉3颗。

30%的测试数据,2<=N<=200<=a[i], x<=100

70%的测试数据,2<=N<=10000<=a[i], x<=10^5

100%的测试数据,2<=N<=10^50<=a[i], x<=10^9
题目

 

贪心,从前往后枚举2个,如果和>x,在后一堆里减去超出的部分

因为只有后一堆才会影响后面的结果,当然是让他越小越好。

注意:有可能后一堆不够减,所以后一堆的剩余的糖果应为max(0,前一堆+后一堆-x)

#include<cstdio>
#include<algorithm>
using namespace std;
int n,x;
long long ans;
int main()
{
    scanf("%d%d",&n,&x);
    int a,b;
    scanf("%d",&a);
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&b);
        if(a+b>x) 
        {
            ans+=a+b-x;
            a=max(x-a,0);
        }
        else a=b;
    }
    printf("%lld",ans);
}

 

T2 P3818 小A和uim之大逃离 II

题目背景

话说上回……还是参见 https://www.luogu.org/problem/show?pid=1373 吧

小a和uim再次来到雨林中探险。突然一阵南风吹来,一片乌云从南部天边急涌过来,还伴着一道道闪电,一阵阵雷声。刹那间,狂风大作,乌云布满了天空,紧接着豆大的雨点从天空中打落下来,只见前方出现了一个牛头马面的怪物,低沉着声音说:“呵呵,既然你们来到这,两个都别活了!”。小a和他的小伙伴再次惊呆了!

题目描述

瞬间,地面上出现了一个H行W列的巨幅矩阵,矩阵的每个格子上要么是空地‘.’或者障碍'#'。

他们起点在(1,1),要逃往(H,W)的出口。他们可以一次向上下左右移动一格,这个算一步操作。不过他们还保留着上次冒险时收集的魔液,一口气喝掉后可以瞬移到相对自己位置的(D,R)向量;也就是说,原来的位置是(x,y),然后新的位置是(x+D,y+R),这个也算一步操作,不过他们仅能至多进行一次这种操作(当然可以不喝魔液)。

这个地方是个是非之地。所以他们希望知道最小能有几步操作可以离开这个鬼地方。不过他们可能逃不出这个鬼地方,遇到这种情况,只能等死,别无他法。

输入输出格式

输入格式:

 

第一行个整数,H W D R,意义在描述已经说明。

接下来H行,每行长度是W,仅有'.'或者'#'的字符串。

 

输出格式:

 

请输出一个整数表示最小的逃出操作次数。如果他们逃不出来,就输出-1。

 

输入输出样例

输入样例#13 6 2 1
...#..
..##..
..#...
输出样例#15
输入样例#23 7 2 1
..#..#.
.##.##.
.#..#..
输出样例#2-1
输入样例#36 6 -2 0
.#....
.#.#..
.####.
.#..#.
.##.#.
....#.
输出样例#321
说明

样例解释1

(1,1)→(1,2)→(1,3)→喝(3,4)→(3,5)→(3,6)

样例解释2

因为只有一瓶魔液所以他们没办法逃出来

样例解释3

D和R还可以是0或者负数。

数据范围与约定

40%的测试数据2<=H,W<=5

70%的测试数据2<=H,W<=100

100%的测试数据2<=H,W<=1000,|D|<H,|R|<W
题目

 

spfa,没什么好说的

开始写的DP,就说有后效性,不明白。。

#include<cstdio>
#include<cstring>
using namespace std;
int h,w,d,r;
bool a[1011][1011],v[2][1001][1001];
char s[1001];
int head,tail,q[2000001][3];
int dp[2][1001][1001];
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
int main()
{
    scanf("%d%d%d%d",&h,&w,&d,&r);
    for(int i=1;i<=h;i++)
    {
        scanf("%s",s);
        for(int j=1;j<=w;j++)
         if(s[j-1]=='.') a[i][j]=true;
    }
    memset(dp,0x3f3f3f3f,sizeof(dp));
    q[0][0]=q[0][1]=1; q[0][2]=0;
    if(a[1+d][1+r]) q[1][0]=1+d,q[1][1]=1+r,q[1][2]=1,dp[1][1+d][1+r]=1,v[1][1+d][1+r]=true;
    v[0][1][1]=true;
    dp[0][1][1]=0;
    tail++; 
    int x,y,use;
    while(head<tail)
    {
        x=q[head][0]; y=q[head][1]; use=q[head][2];
        for(int i=0;i<4;i++)
         if(a[x+dx[i]][y+dy[i]] && dp[use][x][y]+1<dp[use][x+dx[i]][y+dy[i]])
         {
             dp[use][x+dx[i]][y+dy[i]]=dp[use][x][y]+1;
             if(!v[use][x+dx[i]][y+dy[i]])
             {
                 v[use][x+dx[i]][y+dy[i]]=true;
                 q[tail][0]=x+dx[i];
                 q[tail][1]=y+dy[i];
                 q[tail++][2]=use;
            }
         }
        if(!use)
            if(x+d>0 && x+d<=h && y+r>0 && y+r<=w && a[x+d][y+r] && dp[1][x+d][y+r]>dp[0][x][y]+1)
            {
                dp[1][x+d][y+r]=dp[0][x][y]+1;
                if(!v[1][x+d][y+r])
                {
                    v[1][x+d][y+r]=true;
                    q[tail][0]=x+d;
                    q[tail][1]=y+r;
                    q[tail++][2]=1;
                }
            }
        head++; v[use][x][y]=false;
    }
    int ans1=dp[0][h][w],ans2=dp[1][h][w];
    if(ans1==0x3f3f3f3f && ans2==0x3f3f3f3f) printf("-1");
    else printf("%d",ans1<ans2 ? ans1:ans2);
}

 

T3 P3819 松江1843路

题目描述

涞坊路是一条长L米的道路,道路上的坐标范围从0到L,路上有N座房子,第i座房子建在坐标为x[i]的地方,其中住了r[i]人。

松江1843路公交车要在这条路上建一个公交站,市政府希望让最多的人得到方便,因此希望所有的每一个的居民,从家到车站的距离的总和最短。

公交站应该建在哪里呢?

输入输出格式

输入格式:

 

第一行输入L、N。

接下来N行,每行两个整数x[i]和r[i]。

 

输出格式:

一个整数,最小的每个人从家到车站的距离的总和。



输入输出样例

输入样例#1100 3
20 3
50 2
70 1
输出样例#1110

输入样例#2100 2
0 1
100 10
输出样例#2100
输入样例#310000000000 5
3282894320 391
4394338332 929
6932893249 181
7823822843 440
9322388365 623
输出样例#35473201404068
说明

样例解释1

当建在坐标40的时候,所有人距离车站的距离总和为 |20403+|50402+|70401=110。

数据范围和约定

对于10%的数据,1≤N≤50,R[i]=1。

对于30%的数据,1≤N≤100,R[i]≤101≤L≤1000。

对于70%的数据,1≤N≤1000,R[i]≤1001≤L≤10^6。

对于全部数据,1≤L≤10^101≤N≤10^50≤x[i]≤L,1≤r[i]≤1000
题目

 

建的车站一定可以是某居民房的位置

证明:

如果车站在两个居民房中间,

设车站左边有L人,右边有R人

如果L>R,那么车站每左移一个单位,距离减L,加R,更优

如果R>L,那么车站每右移一个单位,距离加L,减R,更优

所以车站一定能建在居民房处

所以先排序,

for循环枚举建在哪儿就好了,

如果已经算出了建在第i-1处的答案,那么建在第i处的答案就是

+ 前i-1处的居民和 * i到i+1的距离,

-  i及i以后的居民和 * i到i+1的距离

 

#include<cstdio>
#include<algorithm>
#define N 100001
using namespace std;
int l,n;
long long tot[N];
long long ans;
struct node
{
    long long pos,sum;
    bool operator < (node p)const
    {
        return pos<p.pos;
    }
}e[N];
long long read(long long &x)
{
    x=0; char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar(); }
}
int main()
{
    scanf("%d%d",&l,&n);
    for(int i=1;i<=n;i++) 
    {
        read(e[i].pos); read(e[i].sum);
        tot[i]=tot[i-1]+e[i].sum;
    }
    sort(e+1,e+n+1);
    for(int i=2;i<=n;i++) ans+=e[i].sum*(e[i].pos-e[1].pos);
    for(int i=2;i<=n;i++) ans=min(ans,ans+tot[i-1]*(e[i].pos-e[i-1].pos)-(tot[n]-tot[i-1])*(e[i].pos-e[i-1].pos));
    printf("%lld",ans);
}

 

T4 P3820 小D的地下温泉

 

题目背景

小D最喜欢泡温泉了。小D找某奸商租下了一块行列的地,左上角为,右下角为。小D本以为这块地里全是温泉,结果这块地极不稳定,曾经发生过一些地形变动,所以其中一些地方全是土。

题目描述

一开始他会告诉你当前这块地的情况,但是小D有一些假操作,希望你操作给他看:

由小D指定个位置,他希望知道其中哪个位置下水泡温泉的范围最大。泡温泉的范围定义为指定位置通过向上下左右四个方向能到达的位置的个数。若询问的位置为土,则范围为0。如果如果有多个位置均为最大,输出给出顺序较前的那个。位置编号为。

由小D指定个位置,他会使用膜法按顺序翻转这个地方的地形。即若原位置是土,则该位置变为温泉;若原位置是温泉,则该位置变为土。因为小D不希望活动范围减少得太快,所以他在将温泉变为土时不会将一个区域分割。
输入输出格式

输入格式:
第一行输入两个整数,,为土地大小。

接下来的行每行输入个字符,为'.'(代表温泉)或'*'(代表土)(不包括引号)

第行输入一个整数,,为操作数量。

接下来的行,每行先读入两个整数和,表示操作类型和指定点的数量,在同一行还有个数,分别表示个操作的位置为。

输出格式:
对于每个操作1,输出询问的答案并换行

输入输出样例

输入样例#15 5
.*...
.****
*....
*****
.....
3
1 2 1 1 1 3
2 1 3 1
1 2 1 1 1 3
输出样例#12
1
说明

对于30%的数据,

对于70%的数据,

对于100%的数据,

数据在windows下制作
题目

 

 

查询连通块的有关信息——并查集

修改操作:

土地改成温泉,题目中说了将温泉变为土时不会将一个区域分割 ,所以直接siz减1

温泉改成土地,另外新建一个点,与上下左右合并

#include<cstdio>
#define N 1000001
using namespace std;
int n,m,cnt;
int fa[N*2],bl[N*2],sum[N*2];
char s[N];
int find(int i)
{
    return fa[i] ? fa[i]=find(fa[i]):i;
}
void work(int x,int y)
{
    if(bl[x*m+y] || s[x*m+y]=='*') return;
    bl[x*m+y]=cnt; sum[cnt]++;
    if(x) work(x-1,y);
    if(y) work(x,y-1);
    if(x+1<n) work(x+1,y);
    if(y+1<m) work(x,y+1);
}
void merge(int x,int y,int xx,int yy)
{
    if(s[xx*m+yy]=='*' || find(bl[x*m+y])==find(bl[xx*m+yy])) return;
    sum[find(bl[xx*m+yy])]+=sum[find(bl[x*m+y])];
    //sum[find(bl[x*m+y])]=0;
    fa[find(bl[x*m+y])]=find(bl[xx*m+yy]);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++) scanf("%s",s+i*m);
    for(int i=0;i<n;i++)
     for(int j=0;j<m;j++)
      if(s[i*m+j]=='.' && !bl[i*m+j])  
      {
             cnt++;
           work(i,j);
      }
    int q,type,x,y,w,tmp,ans; 
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d%d",&type,&w);
        if(type==1)
        {
            tmp=0; ans=1;
            for(int i=1;i<=w;i++)
            {
                scanf("%d%d",&x,&y);
                x--; y--;
                if(s[x*m+y]=='.')
                if(sum[find(bl[x*m+y])]>tmp) { tmp=sum[find(bl[x*m+y])]; ans=i; }
            }
            printf("%d\n",ans);
        }
        else
        {
            for(int i=1;i<=w;i++)
            {
                scanf("%d%d",&x,&y);
                x--; y--;
                if(s[x*m+y]=='.')
                {
                    s[x*m+y]='*';
                    sum[find(bl[x*m+y])]--;
                    //bl[x*m+y]=0;
                }
                else
                {
                    s[x*m+y]='.';
                    bl[x*m+y]=++cnt;
                    sum[cnt]++;
                    if(x) merge(x,y,x-1,y);
                    if(y) merge(x,y,x,y-1);
                    if(x+1<n) merge(x,y,x+1,y);
                    if(y+1<m) merge(x,y,x,y+1);
                }
            }
        }
    }
}

 

posted @ 2017-07-02 10:52  TRTTG  阅读(322)  评论(0编辑  收藏  举报