8.10题解

T1

这题竟然真是贪心,我xxxx,考试的时候本来想到贪心了,结果,我在代码上写了句话“//贪心不太对的样子,跳近一点多跳几个格子,可能可以给下一个青蛙留出落脚点”,我当时一定是脑子废掉了想那么多,然后这题我就一点头绪都没有了,在看了大概一个多小时什么都没打之后,我把它弃掉之后,在交卷前最后20多分钟随便打了个DP?结果RE0了

说说它的贪心吧,对于你每一只青蛙都尽量跳到它可以跳到的最远的地方,毕竟你如果不跳的最远的话,你占的石头就会变多,给后面的青蛙留下的石头就会更少,关于我那个zz的认为贪心不对的想法,我们来想一想,对于每一个青蛙来说他们的跳跃能力是一样的,如果1青蛙可以跳到的最远的点,恰巧是2青蛙能够到达的唯一一个点,只有一种可能情况,就是1青蛙能够跳到的最远的点是他前面第一个点,不然的话,只要1青蛙和最远点之间有一个点,2青蛙就可以落脚,然后就解决只有一个石头的情况,此时那块石头是1,2两只青蛙的最远点,那他俩就是你死我活的关系,所以贪心的正确性我们就yy严格的证明出来了,剩下的就跳跳跳就可以了,当然一个个跳,找最远点还是会T,所以我们需要一个神奇的$STL$:$set$,这样的话我们最终就以$O(nlogn)$的复杂度解决了这道题

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<set>
 5 #define maxn 1001000
 6 using namespace std;
 7 int t;
 8 int a[maxn];
 9 set <int> ss;
10 int main()
11 {
12     scanf("%d",&t);
13     while(t--)
14     {
15         int n,m,d,l,ans=0;  ss.clear();
16         scanf("%d%d%d%d",&n,&m,&d,&l);
17         a[1]=0;  n++;  ss.insert(0);
18         for(int i=2;i<=n;++i)
19         {
20             scanf("%d",&a[i]);
21             ss.insert(a[i]);
22         }
23         a[++n]=l;  ss.insert(a[n]);
24         while(ss.size()>2)
25         {
26             int bj=0;
27             set<int>::iterator qd=ss.begin();
28             while(*qd<a[n])
29             {
30                 set<int>::iterator ls=--ss.upper_bound(*qd+d);
31                 if(*ls==a[n])  {ans++;  break;}
32                 if(*ls+d>=a[n])  {ss.erase(*ls);  ans++;  break;}
33                 if(*ls<=*qd)  {bj=1;  break;}
34                 qd=ls;  ss.erase(*ls);
35             }
36             if(bj==1)  break;
37             if(ans==m)  break;
38         }
39         if(ans>=m)  printf("Excited
");
40         else  printf("%d
",ans);
41     }
42     return 0;
43 }
View Code

T2

学长讲过的原题,我太废物了,不过他的线段树存的东西,我说实话考场想不到,但是其实emm,怎么说呢,想到的话其实还挺好维护的,先说一下维护什么

struct node{
    int zuo,you;//正常线段树记录管辖范围
    int ceng;//zuo到you这个区间中一共有多少层
    ll sum;//zuo到you这么多层中一共有多少量的金坷垃
    ll cut;//zuo到you这个区间中一共要删掉前面多少层
}a[maxm*4];

建树的时候叶子节点正常操作,然后就是给父亲上传节点信息时的$update$操作

void update(int f)
{
    if(a[2*f+1].cut==0)//右儿子不删左儿子
    {
        a[f].cut=a[2*f].cut;
        a[f].ceng=a[2*f].ceng+a[2*f+1].ceng;
        a[f].sum=a[2*f].sum+a[2*f+1].sum;
    }
    else if(a[2*f+1].cut>=a[2*f].ceng)//右儿子把左儿子删完了
    {
        a[f].cut=a[2*f].cut+a[2*f+1].cut-a[2*f].ceng;//已经用左儿子抵消了右儿子的一部分cut
        a[f].ceng=a[2*f+1].ceng;//左儿子被删完了,无法作出贡献
        a[f].sum=a[2*f+1].sum;
    }
    else//删不干净左儿子
    {
        a[f].cut=a[2*f].cut;//右儿子的删除贡献被清干净了
        a[f].ceng=a[2*f].ceng+a[2*f+1].ceng-a[2*f+1].cut;//左儿子被右儿子删掉一部分
        a[f].sum=a[2*f+1].sum+cal(2*f,a[2*f+1].cut);//函数用于查询左儿子被右儿子删掉一部分之后还剩多少量
    }
}

接下来就是$update$函数中的$cal$函数,他实际上通过不停的询问儿子的处理,来计算右儿子删除操作对左儿子造成的影响

int cal(int f,int w)//当前节点被删除,先删当前节点的右儿子,不够删再去删左儿子
{
    //右儿子刚好够删,直接返回左儿子
    if(a[2*f+1].ceng==w)  return a[f].sum-a[2*f+1].sum;
    //右儿子不能被删完,就准备去删右儿子的右儿子
    if(a[2*f+1].ceng>w)  return a[f].sum-a[2*f+1].sum+cal(2*f+1,w);
    //右儿子不够删,带上删剩下的以及右儿子要删左儿子的,去删左儿子
    return cal(2*f,w-a[2*f+1].ceng+a[2*f+1].cut);
}

解释一下为什么左儿子不能直接写$sum$,而是用父节点-右儿子得到,因为左儿子实际上需要被右儿子删除,但当前你的左儿子中并没有计算右儿子的删除贡献,也就是说现在的左儿子的$sum$值并不真实

剩下的就是单点修改以及查询了

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<stack>
 5 #define maxm 200100
 6 #define ll long long
 7 using namespace std;
 8 struct node{
 9     int zuo,you,ceng;
10     ll sum,cut;
11 }a[maxm*4];
12 int m,q;
13 int cal(int f,int w)
14 {
15     if(a[2*f+1].ceng==w)  return a[f].sum-a[2*f+1].sum;
16     if(a[2*f+1].ceng>w)  return a[f].sum-a[2*f+1].sum+cal(2*f+1,w);
17     return cal(2*f,w-a[2*f+1].ceng+a[2*f+1].cut);
18 }
19 void update(int f)
20 {
21     if(a[2*f+1].cut==0)
22     {
23         a[f].cut=a[2*f].cut;
24         a[f].ceng=a[2*f].ceng+a[2*f+1].ceng;
25         a[f].sum=a[2*f].sum+a[2*f+1].sum;
26     }
27     else if(a[2*f+1].cut>=a[2*f].ceng)
28     {
29         a[f].cut=a[2*f].cut+a[2*f+1].cut-a[2*f].ceng;
30         a[f].ceng=a[2*f+1].ceng;
31         a[f].sum=a[2*f+1].sum;
32     }
33     else
34     {
35         a[f].cut=a[2*f].cut;
36         a[f].ceng=a[2*f].ceng+a[2*f+1].ceng-a[2*f+1].cut;
37         a[f].sum=a[2*f+1].sum+cal(2*f,a[2*f+1].cut);
38     }
39 }
40 void build(int f,int l,int r)
41 {
42     a[f].zuo=l;  a[f].you=r;
43     if(l==r)
44     {
45         int k;  ll v;
46         scanf("%d%lld",&k,&v);
47         if(k==0)  {a[f].sum=v;  a[f].ceng=1;}
48         else  a[f].cut=v;
49         return ;
50     }
51     int mid=(l+r)>>1;
52     build(2*f,l,mid);  build(2*f+1,mid+1,r);
53     update(f);
54 }
55 void change(int f,int d,int opt,ll w)
56 {
57     if(a[f].zuo==a[f].you)
58     {
59         if(opt==0)  {a[f].cut=0;  a[f].sum=w;  a[f].ceng=1;}
60         else  {a[f].sum=0;  a[f].ceng=0;  a[f].cut=w;}
61         return ;
62     }
63     int mid=(a[f].zuo+a[f].you)>>1;
64     if(d<=mid)  change(2*f,d,opt,w);
65     else  change(2*f+1,d,opt,w);
66     update(f);
67 }
68 int main()
69 {
70     scanf("%d%d",&m,&q);
71     build(1,1,m);
72     while(q--)
73     {
74         int c,k;  ll v;  scanf("%d%d%lld",&c,&k,&v);
75         change(1,c,k,v);
76         printf("%lld
",a[1].sum);
77     }
78     return 0;
79 }
View Code

T3

T3数据过水,被我用30分的代码水过了,还没理解正解,先留坑,不过话说$getchar$和$putchar$也太快了点吧,整整帮我卡掉3000毫

暴力就他让干什么就干什么,直接模拟就可以了,我大概思考了一下,复杂度应该是$O(qn^2)$的样子,跑了11000过了

 1 //里层也需要翻
 2 #include<cstdio>
 3 #include<iostream>
 4 #define maxn 2100
 5 using namespace std;
 6 int n,m,q;
 7 char ch;
 8 char hs[maxn],hx[maxn],lz[maxn],ly[maxn];
 9 char a[maxn][maxn];
10 int main()
11 {
12     scanf("%d%d%d",&n,&m,&q);
13     for(int i=1;i<=n;++i)
14         for(int j=1;j<=m;++j)
15         {    
16             ch=getchar();
17             while(ch<'0'||ch>'9')  ch=getchar();
18             while(ch>='0'&&ch<='9')  {a[i][j]=ch;  ch=getchar();}
19         }
20     while(q--)
21     {
22         int x,y,len,i,j;  scanf("%d%d%d",&x,&y,&len);
23         while(len>=1)
24         {
25             for(i=y;i<=y+len-1;++i)  {hs[i]=a[x][i];  hx[i]=a[x+len-1][i];}
26             for(i=x;i<=x+len-1;++i)  {lz[i]=a[i][y];  ly[i]=a[i][y+len-1];}
27             for(i=x,j=y+len-1;i<=x+len-1,j>=y;++i,--j)  a[x][j]=lz[i];
28             for(i=y,j=x;i<=y+len-1,j<=x+len-1;++i,++j)  a[j][y]=hx[i];
29             for(i=x+len-1,j=y;i>=x,j<=y+len-1;--i,++j)  a[x+len-1][j]=ly[i];
30             for(i=y+len-1,j=x+len-1;i>=y,j>=x;--i,--j)  a[j][y+len-1]=hs[i];
31             x++;  y++;  len-=2;
32         }
33     }
34     for(int i=1;i<=n;++i)
35     {
36         for(int j=1;j<=m;++j)  {putchar(a[i][j]);  putchar(' ');}
37         puts("");
38     }
39     return 0;
40 }
暴力优化出奇迹
原文地址:https://www.cnblogs.com/hzjuruo/p/11333123.html