Educational Codeforces Round 82 (Rated for Div. 2)

Educational Codeforces Round 82 (Rated for Div. 2) 

来源:https://codeforces.com/contest/1303       

 

A.Erasing Zeroes 

找左右两端的 1,然后统计中间 0 的个数即可. 

#include <cstdio>
#include <cstring>
#include <algorithm> 
#define N 106 
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;   
char str[104];  
void solve() {
    scanf("%s",str+1); 
    int n=strlen(str+1);  
    int mi=N, ma=0; 
    for(int i=1;i<=n;++i) {
        if(str[i]=='1') {
            mi=min(mi, i); 
            ma=max(ma, i); 
        }
    }
    if(mi==N) printf("0\n"); 
    else {
        int cnt=0; 
        for(int i=mi;i<=ma;++i) {
            if(str[i]=='0') ++cnt; 
        }
        printf("%d\n",cnt);  
    }
}
int main() { 
    // setIO("input");   
    int T; 
    scanf("%d",&T); 
    while(T--) solve(); 
    return 0; 
}

  

B.National Project

 显然要贪心先修好的路,然后前面修完整块,最后一个块只修好路. 

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>  
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;     
void solve() {
    ll n,g,b;  
    scanf("%lld%lld%lld",&n,&g,&b);  
    ll need=(n+1)/2;   
    ll base=need/g, res=need%g;   
    ll ans=base*(g+b);     
    if(res==0) printf("%lld\n",max(n, ans-b));  
    else {
        printf("%lld\n",max(n, ans+res));   
    }
}
int main() { 
    // setIO("input");   
    int T; 
    scanf("%d",&T); 
    while(T--) solve(); 
    return 0; 
}

  

C.Perfect Keyboard

显然如果 $\mathrm{s}$ 串中字符 $\mathrm{c}$ 与超过两种字符相邻是不合法的.  

而与 $1$ 种字符相邻的字符一定是答案串的开头.  

随便选一个与 $1$ 种字符相邻的字符串,然后向右拓展即可,拓展方式显然是唯一的.  

若未能拓展完所有出现过的字符则同样不合法. 

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>  
#define N  205  
#define ll long long
#define pb push_back  
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;     
int ch[27][27],vis[30],cnt[30]; 
char str[N]; 
vector<int>g[30],ans;   
void solve() {
    memset(ch, 0, sizeof(ch)); 
    memset(vis, 0, sizeof(vis)); 
    memset(cnt, 0, sizeof(cnt)); 
    for(int i=0;i<28;++i) g[i].clear(); 
    ans.clear();   
    scanf("%s",str+1); 
    int n=strlen(str+1); 
    if(n==1) {
        printf("YES\n"); 
        printf("%c",str[1]);  
        for(int i=0;i<26;++i) 
            if(str[1]-'a' != i) 
                printf("%c",i+'a');   
        printf("\n");   
    }
    else {
        int flag=0; 
        for(int i=2;i<=n;++i) {
            int a=str[i-1]-'a';  
            int b=str[i]-'a';   
            if(!ch[a][b]) cnt[a]++,g[a].pb(b),ch[a][b]=1;  
            if(!ch[b][a]) cnt[b]++,g[b].pb(a),ch[b][a]=1;      
            if(cnt[a]>2||cnt[b]>2) {
                printf("NO\n");  
                return ;  
            }   
        }
 
        if(!flag) {
            int o=-1,num=0; 
            for(int i=0;i<26;++i) {
                if(cnt[i]==1) o=i; 
                if(cnt[i]) ++num;       
            }
            if(o==-1) printf("NO\n"); 
            else {
                int cur=o;  
                vis[o]=1; 
                ans.pb(o);      
                for(int i=2;i<=num;++i) {
                    for(int j=0;j<26;++j) {
                        if(!vis[j] && ch[cur][j]) {
                            ans.pb(j);    
                            vis[j]=1;  
                            cur=j;   
                            break;  
                        }
                    }
                }
                if(ans.size()==num) {
                    printf("YES\n");  
                    for(int i=0;i<ans.size();++i) 
                        printf("%c",ans[i]+'a');  
                    for(int i=0;i<26;++i) 
                        if(!cnt[i]) 
                            printf("%c",i+'a');  
                    printf("\n"); 
                }
                else {
                    printf("NO\n"); 
                }
            }
        }
    }

}
int main() { 
    // setIO("input"); 
    int T; 
    scanf("%d",&T); 
    while(T--) solve(); 
    return 0; 
}

  

D.Fill The Bag

 设 $\mathrm{sum}=\sum_{\mathrm{i=1}}^{\mathrm{m}} \mathrm{a[i]}$.  

显然若 $n \leqslant sum$ 则一定合法,否则不合法.  

先将 $\mathrm{m}$ 个数都选上,然后我们要减掉 $\mathrm{sum-n}$

有两种操作:

1. 将两个 $2^{\mathrm{i}}$ 形式的数字合并,无代价.

2. 将一个 $2^{\mathrm{i}}$ 形式的数字除以 2,代价为 $1$.

不妨从低位向高位考虑.  

若前面的低位需要补一个 $2^{\mathrm{i}}$,则能给就给.  

这样做正确是因为 $2^{\mathrm{i}}$ 做除法覆盖的是一个区间,所以与更靠前的匹配一定最优.   

若当前位还剩一些数,则将这些数合并起来,可以贡献给更高位.  

#include <cstdio>
#include <vector> 
#include <set>
#include <map>
#include <cstring>
#include <algorithm>
#define N 200009 
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;  
ll n,a[N];  
int m,cnt[70];      
map<ll,int>mp;  
void init() {
    for(int i=0;i<=32;++i) 
        mp[1ll<<i]=i;    
}
ll calc(ll x) { 
   //  printf("%lld\n",x); 
    int lst=-1; 
    ll ans=0; 
    for(int i=0;i<=32;++i) {   
        if(cnt[i]&&lst!=-1) {
            --cnt[i],ans+=i-lst; 
            lst=-1; 
        }   
        if((x >> i) & 1)  {
            // 当前需要.  
            if(cnt[i]) --cnt[i];             
            else if(lst==-1) lst=i;   
        }
        cnt[i+1]+=(cnt[i]>>1);  
        // cnt[i]=cnt[i]&1; 
    }
    return ans;  
}
void solve() {
    scanf("%lld%d",&n,&m);
    memset(cnt, 0, sizeof(cnt));   
    for(int i=1;i<=m;++i) {
        scanf("%lld",&a[i]); 
        cnt[mp[a[i]]]++;   
    }
    ll sum=0; 
    for(int i=1;i<=m;++i) sum+=a[i];  
    if(n>sum) printf("-1\n"); 
    else printf("%lld\n",calc(sum-n)); 
} 
int main() { 
    // setIO("input");  
    int T; 
    init();   
    scanf("%d",&T); 
    while(T--) solve(); 
    return 0; 
}

  

E.Erase Subsequences

考虑枚举这个分界点 $\mathrm{p}$,也就是说 $|t|$ 中前 $p$ 个由第一个串构造,$\mathrm{p}$ 后由第二个串构造.   

$\mathrm{f[i][j]}$ 表示考虑完 $s$ 串的前 $\mathrm{i}$ 个位置,匹配完第一个串前 $j$ 个位置的情况下第二个串的最大匹配长度.

总时间复杂度为 $O(n^3)$.   

#include <cstdio>
#include <vector> 
#include <set>
#include <map>
#include <cstring>
#include <algorithm>
#define N 200009 
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;  
ll n,a[N];  
int m,cnt[70];      
map<ll,int>mp;  
void init() {
    for(int i=0;i<=32;++i) 
        mp[1ll<<i]=i;    
}
ll calc(ll x) { 
   //  printf("%lld\n",x); 
    int lst=-1; 
    ll ans=0; 
    for(int i=0;i<=32;++i) {   
        if(cnt[i]&&lst!=-1) {
            --cnt[i],ans+=i-lst; 
            lst=-1; 
        }   
        if((x >> i) & 1)  {
            // 当前需要.  
            if(cnt[i]) --cnt[i];             
            else if(lst==-1) lst=i;   
        }
        cnt[i+1]+=(cnt[i]>>1);  
        // cnt[i]=cnt[i]&1; 
    }
    return ans;  
}
void solve() {
    scanf("%lld%d",&n,&m);
    memset(cnt, 0, sizeof(cnt));   
    for(int i=1;i<=m;++i) {
        scanf("%lld",&a[i]); 
        cnt[mp[a[i]]]++;   
    }
    ll sum=0; 
    for(int i=1;i<=m;++i) sum+=a[i];  
    if(n>sum) printf("-1\n"); 
    else printf("%lld\n",calc(sum-n)); 
} 
int main() { 
    // setIO("input");  
    int T; 
    init();   
    scanf("%d",&T); 
    while(T--) solve(); 
    return 0; 
}

  

F.Number of Components

连通块个数与连通性有关,不妨用并查集来维护连通性.  

对于每种颜色,单独考虑.

将 $\mathrm{col[x][y]}$ 改为 $\mathrm{c}$ 可以看作是 $\mathrm{col[x][y]}$ 的断边与 $\mathrm{c}$ 的加边.

加边好处理,但是断边不好处理.

好在题目保证 $\mathrm{c[i]} \leqslant \mathrm{c[i+1]}$,故对于颜色 $c$ 来说,一定先有加边后有断边. 

这意味着一旦颜色 $c$ 进行断边,则不会再碰到颜色 $c$ 的加边情况. 

将断边操作按照时间倒叙枚举,变成加边,然后对于答案的增量取相反数即可. 

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm> 
#define N 302  
#define ll long long 
#define pb push_back  
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;     
int dx[]={-1, 0, 1, 0};  
int dy[]={0, -1, 0, 1};           
int n,m,Q,scc;  
int p[N*N],a[N][N],id[N][N],is[N][N],ans[2000005];     
struct data {
    int x, y, ti;  
    data(int x=0,int y=0,int ti=0):x(x),y(y),ti(ti){}  
};   
vector<data>add[2000003],del[2000003];  
void init() {
    for(int i=1;i<=n*m;++i) p[i]=i;   
}
int find(int x) {
    return p[x]==x?x:p[x]=find(p[x]);  
}
int merge(int x, int y) {
    x=find(x); 
    y=find(y); 
    if(x==y) return 0; 
    p[x]=y;  
    return 1;  
}
void work(const vector<data>&g, int d) {
    for(int i=1;i<=n;++i) 
        for(int j=1;j<=m;++j) is[i][j]=0; 
    init();  
    for(int i=0;i<g.size();++i) {
        data e=g[i];  
        int x=e.x; 
        int y=e.y; 
        int t=e.ti;  
        int cur=1; 
        is[x][y]=1;    
        for(int j=0;j<4;++j) {
            int xx=x+dx[j]; 
            int yy=y+dy[j]; 
            if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&is[xx][yy]) {
                cur-=merge(id[xx][yy], id[x][y]);   
            }
        }
        ans[t]+=d*cur;    
        // 合并完毕.     
    }
}
int main() { 
    // setIO("input"); 
    scanf("%d%d%d",&n,&m,&Q);       
    init();  
    for(int i=1;i<=n;++i) 
        for(int j=1;j<=m;++j) id[i][j]=++scc;    
    int mx=1;  
    for(int i=1;i<=Q;++i) {
        int x,y,c; 
        scanf("%d%d%d",&x,&y,&c); 
        if(a[x][y]==c) continue;  
        del[a[x][y]].pb(data(x, y, i)); 
        add[a[x][y]=c].pb(data(x, y, i));  
        mx=max(mx, c); 
    }  
    for(int i=1;i<=n;++i) 
        for(int j=1;j<=m;++j) del[a[i][j]].pb(data(i, j, Q+1));       
    for(int i=0;i<=mx;++i) {
        reverse(del[i].begin(), del[i].end());                                             
    }               
    for(int i=0;i<=mx;++i) {  
        work(add[i], +1);                                          
        work(del[i], -1);   
    }
    int fin=1; 
    for(int i=1;i<=Q;++i) {
        fin+=ans[i];  
        printf("%d\n",fin);  
    }
    return 0; 
}

  

G.Sum of Prefix Sums

路径问题考虑用点分治进行处理.  

在处理分治中心的时候只伸向一个子树的路径是好处理的. 

若路径要伸向两个子树,则固定一个子树,对于第二个子树来说深度+1,第一个子树的贡献是线性的.

那么就将第一个子树的贡献看作是一次函数,用李超线段树进行维护.  

特别注意求前缀和的前缀和的最大值时要注意方向,所以要正反跑两次.  

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define N  170002
#define ll long long 
#define pb push_back 
#define ls (now<<1) 
#define rs (now<<1|1)
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;     
vector<int>G[N];   
ll ans,d1[N],d2[N],d3[N],d4[N],a[N];     
int size[N],dep[N],f[N],vis[N],sn,n,root,cnt;               
struct Line {
    ll k,b; 
    Line() { k=b=0; }        
}line[N];  
ll calc(Line o, int pos) {
    return 1ll*o.k*pos+o.b;   
} 
struct SGT {
    ll ma[N<<2]; 
    int tree[N<<2]; 
    vector<int>clr;   
    void modify(int l,int r,int now,int x) {           
        int mid=(l+r)>>1;             
        clr.pb(now);  
        if(calc(line[x], mid) > calc(line[tree[now]], mid))
            swap(tree[now], x);  
        if(l!=r&&calc(line[x], l) > calc(line[tree[now]], l)) 
            modify(l,mid,ls,x); 
        if(l!=r&&calc(line[x], r) > calc(line[tree[now]], r)) 
            modify(mid+1,r,rs,x);  
        if(l!=r) ma[now]=max(ma[now], max(ma[ls], ma[rs]));         
        ma[now]=max(calc(line[tree[now]], l), calc(line[tree[now]], r));          
    }       
    ll query(int l,int r,int now,int L,int R) {    
        if(l>=L&&r<=R) {     
            return ma[now];  
        }
        int mid=(l+r)>>1; 
        ll re=max(calc(line[tree[now]], max(l,L)), calc(line[tree[now]], min(r,R)));     
        if(L<=mid) re=max(re, query(l,mid,ls,L,R)); 
        if(R>mid)  re=max(re, query(mid+1,r,rs,L,R));  
        return re;  
    }
    void CLR() {
        for(int i=0;i<clr.size();++i) {
            tree[clr[i]]=ma[clr[i]]=0;    
        }
        clr.clear();  
    }
}T;  
void getroot(int x, int ff) {
    size[x]=1,f[x]=0; 
    for(int i=0;i<G[x].size();++i) {
        int v=G[x][i];  
        if(v==ff||vis[v]) continue;  
        getroot(v, x); 
        size[x]+=size[v]; 
        f[x]=max(f[x], size[v]); 
    }
    f[x]=max(f[x], sn-size[x]); 
    if(f[x]<f[root]) root=x;    
}
void getans(int x, int ff) {  
    ans=max(ans, T.query(1, n, 1, dep[x], dep[x])+1ll*d1[x]*dep[x]+d2[x]);    
    for(int i=0;i<G[x].size();++i) {      
        int v=G[x][i];  
        if(vis[v]||v==ff) continue;  
        dep[v]=dep[x]+1;  
        d1[v]=d1[x]+a[v];  
        d2[v]=d2[x]+1ll*a[v]*(1-dep[v]);    
        getans(v, x);   
    }    
}
void update(int x, int ff) {
    ++cnt;  
    line[cnt].b=d3[x]; 
    line[cnt].k=(ll)d4[x];   
    T.modify(1,n,1,cnt);  
    ans=max(ans, d3[x]);      
    for(int i=0;i<G[x].size();++i) {
        int v=G[x][i]; 
        if(vis[v]||v==ff) continue;   
        d3[v]=d3[x]+1ll*a[v]*(dep[v]+1);     
        d4[v]=d4[x]+a[v];   
        update(v, x);   
    }
}
void dfs(int x) {   
    // 将 x 插入.  
    vis[x]=1; 
    cnt=1;   
    line[cnt].b=a[x];     
    line[cnt].k=a[x];       
    T.modify(1, n, 1, cnt);        
    for(int i=0;i<G[x].size();++i) {
        int v=G[x][i]; 
        if(vis[v]) continue;  
        dep[v]=1;  
        d1[v]=a[v];       
        d2[v]=0;              
        getans(v, x); 
        d3[v]=a[x]+2ll*a[v];   
        d4[v]=a[x]+a[v];   
        update(v, x);                  
    }   
    T.CLR();   
    cnt=1;  
    line[cnt].b=a[x]; 
    line[cnt].k=a[x];
    T.modify(1,n,1,cnt);  
    for(int i=G[x].size()-1;i>=0;--i) {
        int v=G[x][i];  
        if(vis[v]) continue; 
        dep[v]=1;  
        d1[v]=a[v];       
        d2[v]=0;              
        getans(v, x); 
        d3[v]=a[x]+2ll*a[v];   
        d4[v]=a[x]+a[v];   
        update(v, x);  
    }
    T.CLR(),cnt=1;  
    for(int i=0;i<G[x].size();++i) {
        int v=G[x][i];   
        if(vis[v]) continue;  
        sn=size[x], root=0; 
        getroot(v, x); 
        dfs(root);  
    }   
}
int main() {
    // setIO("input"); 
    scanf("%d",&n); 
    for(int i=1;i<n;++i) {
        int x,y; 
        scanf("%d%d",&x,&y); 
        G[x].pb(y); 
        G[y].pb(x);  
    }
    for(int i=1;i<=n;++i) {
        scanf("%lld",&a[i]);     
        ans=max(ans, a[i]); 
    }
    sn=n; 
    f[root=0]=N; 
    getroot(1, 0);    
    dfs(root);  
    printf("%lld\n",ans);     
    return 0; 
}

  

 

posted @ 2021-09-07 17:42  guangheli  阅读(39)  评论(0)    收藏  举报