首页 > 其他分享 >Codeforces Round 906 Div. 1 (CF1889)

Codeforces Round 906 Div. 1 (CF1889)

时间:2023-10-31 21:14:53浏览次数:36  
标签:le 906 删除 覆盖 int 线段 CF1889 st Div

貌似现在发周六的 CF 题解已经失去了时效性,不过问题不大。

A. Qingshan Loves Strings 2

Description

定义一个长度为 \(k\) 的 \(01\) 串 \(s\) 是好的,当且仅当 \(\forall i\in [1,k],s_i\neq s_{k-i+1}\)。
现给你一个串,每次操作你可以在任意位置插入一对 \(01\)。请构造操作方案使串变成好的,或判断无解。多组询问。

Solution

div1 A 想半天,鉴定为没救了。

首先发现,一个好的串中 \(0\) 和 \(1\) 的数量一定相等,而插入操作不改变两种字符的数量差。那么如果初始的串 \(0\) 和 \(1\) 数量不等,则无解。
手玩几个小数据,我们猜测其余情况都是有解的。由于将 \(01\) 插在串中间不会改变开头和结尾字符的配对情况,考虑从两端开始配对:

  • 若字符串的开头和结尾不相同,则忽略它们,用同样的方法处理 \([2,k-1]\);
  • 否则根据都是 \(0\) 还是 \(1\) 分讨,在字符串的开头或结尾添加一对 \(01\)。

容易发现这样一定可以构造出一种可行解。

Code
int T,n;
string s;
vector<int> ans;
void solve(string s)
{
    // cout<<s<<endl;
    int n=s.size();
    for(int i=0;i<n;i++) if(s[i]==s[n-i-1])
    {
        if(s[i]=='0') 
        {
            ans.push_back(n-i);
            string t;
            for(int j=0;j<n-i;j++) t+=s[j];
            t+="01"; for(int j=n-i;j<n;j++) t+=s[j];
            solve(t); return;
        }
        else
        {
            ans.push_back(i);
            string t;
            for(int j=0;j<i;j++) t+=s[j];
            t+="01"; for(int j=i;j<n;j++) t+=s[j];
            solve(t); return;
        }
    }
}
int main()
{
    T=read();
    while(T--)
    {
        n=read(); cin>>s;
        int cnt=0;
        for(int i=0;i<n;i++) if(s[i]=='1') cnt++;
        if(2*cnt!=n) {printf("-1\n");continue;}
        ans.clear();
        solve(s);
        printf("%d\n",(int)ans.size());
        for(auto x:ans) printf("%d ",x); printf("\n");
    }
    return 0;
}

B. Doremy's Connecting Plan

首先 \(c\) 啥用没有,因为可以移项,令每个点的初始权值为 \(\frac{a_i}{c}\)。
自信猜结论。如果第一步 \(x,y\) 能连边,那么 \(1,x\) 或者 \(1,y\) 一定有一个能连边,且这样做是不劣的。
因此最优策略是每个点都跟 \(1\) 连边。式子是 \(s_1+a_i\ge 1\times i\),按 \(i-a_i\) 升序排序贪心即可。

Code
#define int long long
const int N=2e5+5;
int T,n,c;
struct node
{
    int a,w;
    friend bool operator <(const node &x,const node &y) {return x.w<y.w;}
}a[N];
signed main()
{
    T=read();
    while(T--)
    {
        n=read(),c=read();
        for(int i=1;i<=n;i++) a[i].a=read();
        for(int i=2;i<=n;i++) a[i].w=c*i-a[i].a;
        sort(a+2,a+n+1);
        double now=a[1].a;bool flag=1;
        for(int i=2;i<=n;i++)
        {
            if(now<a[i].w) {flag=0;break;}
            now+=a[i].a;
        }
        printf(flag?"Yes\n":"No\n");
    }
    return 0;
}

C1. Doremy's Drying Plan (Easy Version)

Description

给定 \(m\) 条线段 \([l_i,r_i]\),其中 \(l_i,r_i\in [1,n]\)。至多删除 \(k\) 条线段,使 \([1,n]\) 中未被任何线段覆盖的点数最多。输出这个数。
\(n,m\le 2\times 10^5,\, k=2\)。

Solution

观察到 \(k\) 只有 \(2\),这意味着如果某点 \(i\) 被 \(\ge 3\) 条线段覆盖,这个点无论如何最后都还是被覆盖,不需要考虑它对答案的贡献。
而一开始就不被覆盖的点也是容易计算的,可以直接加进答案。
那么剩下的点对答案有贡献的情况只分两种:

  • 点 \(i\) 被覆盖了两次,且这两条线段都被删了。
  • 点 \(i\) 被覆盖了一次,且这条线段被删了。

因此被删除的线段也只有两种情况:

  • 如果这两条线段造成了第一类点的贡献,直接枚举每个可能的点,并尝试删除覆盖它的两条线段。
  • 否则,这两条线段造成了两个第二类点的贡献。预处理出每条线段删除的贡献,选最大和次大值。

第二类是容易统计答案的,我们考虑第一类怎么统计。删除两条线段对答案造成的贡献即为恰好只被其中一条覆盖的点数与恰好被它们两个覆盖的点数之和。前者在维护第二类的时候已经求了。而这样的线段至多有 \(n\) 对,可以暴力地维护后者。

求每个点被哪些线段覆盖可以把 \([l,r]\) 拆成 \(l\) 和 \(r+1\),multiset 维护。

C2. Doremy's Drying Plan (Hard Version)

Description

给定 \(m\) 条线段 \([l_i,r_i]\),其中 \(l_i,r_i\in [1,n]\)。至多删除 \(K\) 条线段,使 \([1,n]\) 中未被任何线段覆盖的点数最多。输出这个数。

\(n,m\le 2\times 10^5,\, K\le \min(10,m)\)。

Solution

推翻 C1 的做法,考虑 DP。
设 \(f_{i,j}\) 表示考虑了点 \(i\) 不被覆盖,已经删除了 \(j\) 条线段,前 \(i\) 个点最多不被覆盖的点数。
那么我们枚举上一个没被覆盖的点为 \(k\),考虑所有覆盖的点 \(i\) 的线段 \([l,r]\):

  • 如果 \(l\le k\),由于要保证 \(k\) 没被覆盖,这条线段在 \(k\) 那里就被删了,不应该计入“令点 \(i\) 不被覆盖”的代价。
  • \(l>k\),说明这条线段是因为点 \(i\) 而删掉的,应当计入代价。

设第二类线段数为 \(t\)。那么有转移:

\[f_{i,j}=1+\max\limits_{k=0}^{i-1} f_{k,j-t} \]

直接实现是 \(O(n^2k)\) 的,考虑找性质优化。发现第二维只有 \(K\) 种取值,且 \(j-t\) 的取值随 \(k\) 的增加单调不降。那么我们可以根据 \(j-t\) 的取值将 \(k\) 划分成 \(K\) 段。
对于每个 \(j\) 使用数据结构维护区间最大值即可,但是有同层转移,需要支持修改操作,而线段树等数据结构都是 \(O(nk^2 \log n)\) 的,无法接受。
这个修改操作实际上只会依次在末尾加数,而不会改变已经添加过的值。
这可以使用 ST 表维护:把 ST 表倒过来,令 \(st_{i,j}\) 表示 \([j-2^i+1,j]\) 的最大值。这样即可方便地在末尾以 \(\log n\) 的复杂度更新 ST 表。

时间复杂度 \(O(nk^2)\)。

Code
const int N=2e5+5,M=15,inf=1e9;
int T,n,m,k;
vector<int> t1[N],t2[N];
multiset<int> s;
vector<int> a[N];
int f[M][N];
struct ST
{
    int st[20][N];
    void ins(int x,int i)
    {
        st[0][i]=f[x][i];
        for(int j=1;i-(1<<j-1)>=0;j++) st[j][i]=max(st[j-1][i],st[j-1][i-(1<<j-1)]);
    }
    il int ask(int l,int r)
    {
        if(l>r) return -inf;
        int len=__lg(r-l+1);
        return max(st[len][r],st[len][l+(1<<len)-1]);
    }
}st[M];
int main()
{
    T=read();
    while(T--)
    {
        n=read(),m=read(),k=read();
        for(int i=1;i<=n;i++) t1[i].clear(),t2[i].clear(),a[i].clear();
        s.clear();
        for(int i=1;i<=m;i++)
        {
            int l=read(),r=read();
            t1[l].push_back(l),t2[r+1].push_back(l);
        }
        for(int i=1;i<=n;i++)
        {
            for(auto x:t1[i]) s.insert(x);
            for(auto x:t2[i]) s.erase(s.find(x));
            if(s.size()>k) a[i].push_back(-1);
            else {for(auto x:s) a[i].push_back(x); a[i].push_back(i);}
        }
        for(int i=1;i<=n;i++) f[0][i]=f[0][i-1]+(a[i].size()==1&&a[i][0]!=-1);
        st[0].ins(0,0);
        for(int i=1;i<=n;i++) 
        {
            if(!a[i].empty()&&a[i][0]==-1) f[0][i]=0;
            st[0].ins(0,i);
        }
        for(int j=1;j<=k;j++)
        {
            for(int i=1;i<=n;i++)
            {
                int lst=0,now=a[i].size()-1; f[j][i]=0;
                if(a[i].size()==1&&a[i][0]==-1) {st[j].ins(j,i);continue;}
                for(auto x:a[i])
                {
                    if(j-now<0) break;
                    f[j][i]=max(f[j][i],1+st[j-now].ask(lst,x-1));
                    now--,lst=x;
                }
                st[j].ins(j,i);
            }
        }
        printf("%d\n",st[k].ask(0,n));
    }
    return 0;
}

D. Game of Stacks

我是鸽子。会补的。

标签:le,906,删除,覆盖,int,线段,CF1889,st,Div
From: https://www.cnblogs.com/ying-xue/p/cf1889.html

相关文章

  • Codeforces Round 906 (Div. 2)A-E1
    A.Doremy'sPaint3记数组中数的种类数为\(k\),当\(k=1\)时,答案为\(yes\);当\(k=2\)时,记两个种类的数的个数差为\(d\),当\(d≤1\)时,答案为\(yes\);其他情况答案为\(no\)。时间复杂度:\(O(nlogn)\)1voidsolve()2{3intn;cin>>n;45map<int,int>mp;6......
  • CF1889B
    题面给一个\(n\)个点的图,每个点\(i\)有点权\(a_i\),初始图上没有边,你可以进行如下操作若干次:若\(S_i+S_j\gei\timesj\timesc\),添加一条边\((i,j)\)。其中\(S_i\)表示\(i\)所在连通块的点权和,\(c\)是一个给定的常数。问最终能否使图联通。首先我们有一个简......
  • CF1889D
    题意给定\(n\)个栈,栈的大小分别为\(k_i\),每个栈内元素\(\in[1,n]\),记从第\(i\)个栈开始的答案为\(ans_i\),流程:若栈\(i\)为空,答案为\(i\);否则弹出栈顶元素\(x\),并前往栈\(x\),继续刚才的操作。\(n\le10^5,\sumk_i\le10^6\)做法考虑弱化情况,\(k_i=1\),记栈\(i\)元素为\(p_i\)......
  • 视频直播app源码,CSS div水平垂直居中和div置于底部
    视频直播app源码,CSSdiv水平垂直居中和div置于底部一、水平居中 .hor_center{  margin:0auto;}​二、水平垂直居中 .content{  width:360px;  height:240px;} .ver_hor_center{  position:absolute;  top:50%;  left:50%;  margi......
  • Codeforces Round 906 (Div. 2) A-E1
    比赛地址A.Doremy'sPaint3题意:给出一个数组\(b\),问能否通过重新排序使得数组满足\(b_1+b_2=b_2+b_3=...=b_{n-1}+b_{n}\)Solution首先判断元素个数如果是1,则满足条件如果是2,需判断不同元素个数的差是否小于等于1其余的均不满足条件voidsolve(){intn;cin>......
  • CF1889C2. Doremy's Drying Plan (Hard Version)
    容易想到dp:设\(dp_{i,p}\)表示前\(i\)天,强制第\(i\)天dry,并且一共消除了\(p\)个区间的答案。转移时可以考虑枚举前面的决策\(j\),此时有转移方程:\[dp_{i,p}=\max(dp_{j,p-w})+1\]其中\(w\)为满足\(l\in(j,i],r\in[i,n]\)的区间\([l,r]\)个数。显然可以考虑套......
  • CF1889D. Game of Stacks
    啊啊啊每次补完题都感觉这题我场上应该是能想出来的啊!考虑简化问题:若每个栈内只有一个元素,how。此时我们发现所有栈之间构成了一个内向基环树。且环是没有用的,因为我们在环上走一圈之后仍然会回到原点。所以不妨把所有环边删除,此时每棵树的答案即为树根。而对于原问题,同理,我们......
  • CF1889A. Qingshan Loves Strings 2
    不妨考虑什么时候会无解!显然当原序列\(0,1\)数量不同,或者序列总长为奇数时会无解!否则我们设\(l=1,r=n\)!开始回文配对!如果配上了就直接删掉!并把左右端点向内移动!如果两者都是\(0\),就在末尾加上\(01\)!都是\(1\)就加最前面!以在末尾加入举例!此时如果开头是\(01\)就会多......
  • CF1889B. Doremy's Connecting Plan
    一开始不会先跳C了!差点满盘皆输!设\(i<j\),则\(i,j\)合并可以看作\(a_i\leftarrowa_i+a_j\)后删掉\(j\)!此时和初始局面本质相同!所以不妨先只看初始局面!不等式右侧和下标有关!显然若右侧\(i,j\)中只要有一个是\(1\),就会让右侧的值大幅减小!设\(1\)和\(i\)合并!则需满......
  • 无涯教程-Clojure - Accessing Individual Fields函数
    可以通过与结构对象一起访问键来访问结构的各个字段。AccessingIndividual-语法:keystructure-name参数   - "key"是结构中的键值,"structure-name"是作为相应关键字的结构。返回值 - 将返回与键关联的值。以下程序显示了有关如何使用它的示例。AccessingI......