Codeforces Round #476 (Div. 2) [Thanks, Telegram!] 题解

A. Paper Airplanes
简单数学模拟题。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

ll k,n,s,p;
int main(){
    scanf("%I64d%I64d%I64d%I64d",&k,&n,&s,&p);
    ll each=ceil(1.0*n/s);
    ll tot=each*k;
    ll ans=1ll*ceil(1.0*tot/p);
    cout<<ans<<endl;
    return 0;
}

B. Battleship
在一个 nn n ∗ n 的网格图中,一些点具有障碍。可在空矿的地方放置一艘船,船长 k k 即需要有连续k个空位。
求被最多方案覆盖的方格。
n,k<=100
一艘船覆盖的地方显然都是空格,这些格子答案都加 1 1
然后我们可以把障碍和空格分别看成1 0 求前缀和即可快速获得这个地方能不能放船。
问题就变成了多次区间加,最后查询最大值。差分即可。

#include<bits/stdc++.h>
using namespace std;

const int MAXN=105;

int mmap[MAXN][MAXN],sum[MAXN][MAXN],cf[MAXN][MAXN],ans[MAXN][MAXN],ans2[MAXN][MAXN],h=1,l=1,ansp=0,n,k;
char s[MAXN];

int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%s",s);
        for(int j=0;j<n;j++){
            if(s[j]=='#')mmap[i][j+1]=1;
            else mmap[i][j+1]=0;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            sum[i][j]=sum[i][j-1]+mmap[i][j];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(j+k-1>n)break;
            if(sum[i][j-1]==sum[i][j+k-1])cf[i][j]++,cf[i][j+k]--;          
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            ans[i][j]=ans[i][j-1]+cf[i][j];
        }
    }
    memset(sum,0,sizeof(sum));
    memset(cf,0,sizeof(cf));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            sum[j][i]=sum[j-1][i]+mmap[j][i];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(j+k-1>n)break;
            if(sum[j-1][i]==sum[j+k-1][i])cf[j][i]++,cf[j+k][i]--;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            ans2[j][i]=ans2[j-1][i]+cf[j][i];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(ans[i][j]+ans2[i][j]>ansp){ 
                ansp=ans[i][j]+ans2[i][j],h=i,l=j;
            }
        }
    }
    cout<<h<<" "<<l<<endl;
    return 0;
}

C. Greedy Arkady
k 个小朋友围成一圈分 n n 个糖 按照 1k1 的顺序轮流给糖。每次给 x x 块糖 不足 x 块糖则丢掉结束。
规定 x<=M x <= M 且 没有小朋友分到的糖的次数比D多(圈数)。求 1 1 号小朋友最多分到多少颗糖。

(2n1018,2kn,1Mn,1Dmin(n,1000),MDkn)

发现D非常小。
如果D已知,我们可以贪心求出 1 1 号小朋友的糖数。就是 1 号小朋友拿 D D 次糖,其他人拿 D1次糖。

得到 Dx+(D1)(k1)x<=n D x + ( D − 1 ) ( k − 1 ) x <= n
Dkxkx+x<=n D k x − k x + x <= n
x(Dkk+1)<=n x ( D k − k + 1 ) <= n
x<=n/(Dkk+1) x <= n / ( D k − k + 1 )
x x n/(Dkk+1) 时最优。枚举每个 D D 求答案。
注意特判x==m
x==0 x == 0 结束输出答案

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,k,m,d,ans=0;
inline ll read(){
    char c=getchar();ll x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f; 
}
//(i-1)kx + x <= n
//kix -kx +x<=n
//(ki-k+1)x<=n
//n/(ki-k+1)=x
//n/(k(i-1)+1)=x

int main(){
    n=read();k=read();m=read();d=read();
    ans=ceil((n/m)/(1.0*k))*m;
    for(int i=1;i<=d;i++){
        ll x=n/(k*(i-1)+1);
        if(x==0)break;
        if(x>m)continue;
        ans=max(ans,1ll*i*x);
    }
    cout<<ans<<endl;
    return 0;
}
/*
*/

D. Single-use Stones
有一些青蛙过河。河宽 w w . 他们每次可以跳 [1,l] 区间内任意长度。河上每个位置 i i ai个 石头,踩一下就会沉下去。最大化青蛙过河的只数。
w<=105ai<=104 w <= 10 5 a i <= 10 4
在河岸的青蛙会跳满目前所能跳到的所有石头。即使他们最终无法到达河对岸。这显然是最优的。
对于位置 i i 上的青蛙 他可以跳到 区间内有石头的任意位置,但显然最右边的石头是最优的。
我们发现 从 i i+1 i + 1 的青蛙,他们所能跳到的区域之相差左右两端。有 l2 l − 2 个是重复的。
所以这个东西。。我们可以直接 开 deque d e q u e 贪心模拟 线性复杂度。每个点最多进队一次出队一次。

#include<bits/stdc++.h>
using namespace std;

const int MAXN=1e5+5;
int a[MAXN],csd[MAXN],ans=0,w,l;

struct data{
    int pos,remain;
};
deque<data>q;

int main(){
    scanf("%d%d",&w,&l);
    for(int i=1;i<w;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=l;i++){
        csd[i]=a[i];
    }
    for(int i=1;i<=w;i++){
        int to=i+l;
        while(q.size()&&i>=q.front().pos)q.pop_front();
        if(to<w&&a[to])q.push_back((data){to,a[to]});
        if(csd[i]){
            if(i+l>=w){ans+=csd[i];continue;}
            while(q.size()&&csd[i]){
                if(csd[i]>=q.back().remain){
                    csd[i]-=q.back().remain;//
                    csd[q.back().pos]+=q.back().remain;
                    q.pop_back();
                }
                else{
                    int v=q.back().pos,num=q.back().remain-csd[i];
                    csd[v]+=csd[i];
                    csd[i]=0;
                    q.pop_back();
                    q.push_back((data){v,num});
                }
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}
// front -> back
// X 0 4 1 3 1

E - Short Code
n n 个单词,我们可以把每个单词用他的某个前缀来表示。但要求不能有任意两个单词表示方法相同。最小化这些前缀长度和。

n<=105
len<=105 ∑ l e n <= 10 5
前缀问题很容易想到 trie t r i e 树,我们把单词建 trie t r i e 树发现问题转化为。对于每个 val==1 v a l == 1 的节点,找到一个祖先与之匹配,最小化祖先深度和。显然有一个贪心就是尽量把树的上面放满。我们思考对于一个所有子树已经最优的情况 根是空着的。我们显然要把子树中关键点最深的提到根。这样达到最优。如果根也有值,那么这个子树直接就是最优的。
所以这个东西转化成了按照从叶子向根的顺序 单点修改子树查询。线段树可以解决。
但是 由于这个问题是在树中的qwq, 可以记录 最大值 次大值来 DP D P ? 或许? 瞎口胡的qwq。

#include<bits/stdc++.h>
using namespace std;

const int MAXN=1e5+10;

int c[MAXN][26],cnt=1,size[MAXN],fa[MAXN],top[MAXN],dep[MAXN],hson[MAXN],val[MAXN];

void insert(char *s){
    int len=strlen(s),now=1;
    for(int i=0;i<len;i++){
        int v=s[i]-'a';
        if(!c[now][v])c[now][v]=++cnt;
        now=c[now][v];
    }
    val[now]++;
}
void dfs1(int u,int father){
    dep[u]=dep[father]+1;
    fa[u]=father;
    size[u]=1;
    for(int i=0;i<26;i++){
        if(c[u][i]){
            int v=c[u][i];
            dfs1(v,u);
            size[u]+=size[v];
            if(!hson[u]||size[hson[u]]<size[v])hson[u]=v;
        }
    }
}
int rl[MAXN],rk[MAXN],tim=0;
void dfs2(int u,int tp){
    rk[u]=++tim;
    rl[tim]=u;
    top[u]=tp;
    if(hson[u])dfs2(hson[u],tp);
    for(int i=0;i<26;i++){
        if(c[u][i]){
            int v=c[u][i];
            if(v==hson[u])continue;
            dfs2(v,v);
        }
    }
}

typedef pair<int,int>par;
#define mp make_pair 
struct xds{
    #define lson (o<<1)
    #define rson (o<<1|1)
    int maxdep[MAXN<<2],post[MAXN<<2],sumv[MAXN<<2];//pos 真实的节点
    void pushup(int o){
        maxdep[o]=0,post[o]=-1;
        if(maxdep[lson]>maxdep[o])maxdep[o]=maxdep[lson],post[o]=post[lson];
        if(maxdep[rson]>maxdep[o])maxdep[o]=maxdep[rson],post[o]=post[rson];
        sumv[o]=sumv[lson]+sumv[rson];
    }
    void build(int o,int l,int r){
        if(l==r){
            if(val[rl[l]]){
                sumv[o]=maxdep[o]=dep[rl[l]];
                post[o]=rl[l];
            }
            else sumv[o]=maxdep[o]=0,post[o]=-1;
            return;
        }
        int mid=l+r>>1;
        build(lson,l,mid);build(rson,mid+1,r);
        pushup(o);
    }
    void change(int o,int l,int r,int pos,int ty){//填id
        if(l==r){
            if(ty){
                sumv[o]=maxdep[o]=dep[rl[l]];
                post[o]=rl[l];
            }
            else sumv[o]=maxdep[o]=0,post[o]=-1;
            return ;
        }
        int mid=l+r>>1;
        if(pos<=mid)change(lson,l,mid,pos,ty);
        else change(rson,mid+1,r,pos,ty);
        pushup(o);
    }
    par query(int o,int l,int r,int ql,int qr){
        if(ql<=l&&qr>=r) return mp(maxdep[o],post[o]);
        int mid=l+r>>1;par t1=mp(0,-1),t2=mp(0,-1);
        if(ql<=mid)t1=query(lson,l,mid,ql,qr);
        if(qr>mid)t2=query(rson,mid+1,r,ql,qr);
        if(t1.first>t2.first)return t1;
        return t2;
    }
}T;

void getans(int u){
    for(int i=0;i<26;i++){
        if(c[u][i]){
            getans(c[u][i]);    
        }
    }
    if(!val[u]){
        par tem=T.query(1,1,cnt,rk[u],rk[u]+size[u]-1);
        if(tem.first){
            T.change(1,1,cnt,rk[tem.second],0);
            T.change(1,1,cnt,rk[u],1);
        }
    }
}

int n;
char qwq[MAXN];

int main(){
    scanf("%d",&n); 
    for(int i=1;i<=n;i++){
        scanf("%s",qwq);    
        insert(qwq);
    }
    dep[0]=-1;
    val[1]=1;
    dfs1(1,0);
    dfs2(1,0);
    T.build(1,1,cnt);
    //cout<<T.maxdep[1]<<" "<<T.pos[1]<<endl;
    getans(1);
    cout<<T.sumv[1]<<endl;
    return 0;
}

/*
           null
         c
        o 
       d
      e
     f h
    o   o
   r     r
  c       c
 e         e
1s           1s 



                 null
                  a
               1a  1b
              c     1b
             a       1a
            d
          1a   
                   0 
                 0   1 
                2 3   1
               1   2   1
                    1   1
                         1
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值