『解题报告』CF1582 (Codeforces Round #750 (Div. 2))

感觉这场 div 2 跟 div 3 难度差不多

『比赛链接』

A. Luntik and Concerts

『题目链接』

题目难度\(800\)(模拟)

题目大意

你有 \(A\) 个时长为 \(1\ min\) 的音乐,有 \(B\) 个时长为 \(2\ min\) 的音乐,有 \(C\) 个时长为 \(3\ min\) 的音乐,将这些音乐划分为两个集合,是每个集合的总时长差的绝对值最小。找到最小值。

思路

按照题目分类讨论贪心选取即可。

代码

#include<bits/stdc++.h>
using namespace std;
int t,a,b,c;
int res;
inline void Read()
{
    scanf("%d%d%d",&a,&b,&c);
}
inline void Work()
{
    if(c&1)
    {
        if(b!=0) puts( a&1 ? "0" : "1");
        else
        {
            if(a<=3) printf("%d\n",3-a);
            else puts( a&1 ? "0" : "1");
        }
    }
    else
    {
        if(b&1)
        {
            if(a<=2) printf("%d\n",2-a);
            else puts(a&1 ? "1" : "0");
        }
        else puts( a&1 ? "1" : "0");
    }
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        Read();
        Work();
    }
    return 0;
}

B. Luntik and Subsequences

『题目链接』

题目难度\(900\)(规律)

题目大意

你有一个长度为 \(n\) 的序列和为 \(s\) ,求这个序列有多少个子序列的和为 \(s-1\)

思路

由题意可知我们要删去序列中的一个 \(1\) 和若干个 \(0\) 。若一个序列中有 \(num_0\)\(0\) ,有 \(num1\)\(1\) ,那么答案就是 \(num_1 \times 2^{num_0}\)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=65;
int t,n;
int num1;
ll res;
inline void Read()
{
    num1=0,res=1;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        static int a;
        scanf("%d",&a);
        if(a==0) res<<=1;
        if(a==1) num1++;
    }
}
inline void Work()
{
    printf("%lld\n",num1*res);
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        Read();
        Work();
    }
    return 0;
}

C. Grandma Capa Knits a Scarf

『题目链接』

题目难度\(1200\)(枚举)

题目大意

你有一个字符串 \(s\) ,可以选择一个字母并删去 \(s\) 中的若干个这个字母,问最少删多少个字母可以让 \(s\) ,变为一个回文串

思路

我们可以枚举我们要删去的每种字母,对于每个字母,对 \(s\) 做一遍双指针。

若此时 \(s_i=s_j\) 则继续匹配,否则判断 \(s_i\)\(s_j\) 中有没有枚举的要删去的字母若有,则继续匹配,若没有则无解。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int t,n;
char str[N];
inline int Check(char del)
{
    int i=1,j=n,res=0;
    while(i<=j)
    {
        if(str[i]==str[j]) i++,j--;
        else if(str[i]==del) i++,res++;
        else if(str[j]==del) j--,res++;
        else return -1;
    }
    return res;
}
inline void Read()
{
    scanf("%d",&n);
    scanf("%s",str+1);
}
inline void Work()
{
    int ans=-1;
    for(int i=0;i<26;i++)
    {
        int tmp=Check(i+'a');
        if(tmp!=-1)
        {
            if(ans==-1) ans=tmp;
            ans=min(ans,tmp);
        }
    }
    printf("%d\n",ans);
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        Read();
        Work();
    }
    return 0;
}

D. Vupsen, Pupsen and 0

『题目链接』

题目难度\(1600\)(构造)

题目大意

你有一个长为 \(n\) 的序列 \(a\),你要构造出一个序列 \(b\) 使 \(\sum_{i=1}^n a_ib_i=0\) ,并且 \(b_i\neq0\)

思路

对于 \(n\) 为偶数时,我们可以直接令 $b_i=a_{i+1}\ ,\ b_{i+1}=-a_i $ 即可。

对于 \(n\) 为奇数时,我们可以挑出最后三个来分类讨论。

如果 \(n\leq 2e4\) 时,(由于题目特性)可以直接令 \(b_{n-2}=a_{n-1}\ ,\ b_{n-1}=-a_n-a_{n-2}\ ,\ b_n=a_{n-1}\) 注意当 \(-a_n-a_{n-2}=0\) 时还需在将 \(b_{n-1}-a_{n}\) ,将 \(b_n+a_{n-1}\)

如果 \(n>2e4\) 时,(由于题目特性)一定会有三个 \(a_i\) 相等。

所以将它们特判,使之 \(b_i=1\ ,\ b_{i+1}=-2\ ,\ b_{i+2}=1\) 即可。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int t,n;
int a[N],b[N],c[N];
unordered_map<int,int> siz;
inline void Read()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
}
inline void Work()
{
    if(n&1)
    {
        if(n<=2e4)
        {
            for(int i=1;i<=n-3;i+=2)
                b[i]=-a[i+1],b[i+1]=a[i];
            b[n-2]=a[n-1],b[n-1]=-a[n-2]-a[n],b[n]=a[n-1];
            if(b[n-1]==0) b[n-2]+=a[n-1],b[n-1]-=a[n-2];
        }
        else
        {
            siz.clear();
            for(int i=1;i<=n;i++) siz[a[i]]++;
            for(int i=1;i<=n;i++) c[i]=i;
            sort(c+1,c+n+1,[](int x,int y){return siz[a[x]]==siz[a[y]] ? a[x] < a[y] : siz[a[x]] < siz[a[y]];});
            for(int i=1;i<=n-3;i+=2)
                b[c[i]]=-a[c[i+1]],b[c[i+1]]=a[c[i]];
            b[c[n-2]]=1,b[c[n-1]]=-2,b[c[n]]=1;
        }
    }
    else
    {
        for(int i=1;i<=n;i+=2)
            b[i]=-a[i+1],b[i+1]=a[i];
    }
    for(int i=1;i<=n;i++)
        printf("%d ",b[i]);
    putchar('\n');
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        Read();
        Work();
    }
    return 0;
}

E. Pchelyonok and Segments

『题目链接』

题目难度 \(2000\)(DP)

题目大意

你有一个长为 \(n\) 的序列,你需要按顺序选 \(k\) 个长度分别为 \(k,k-1,\cdots,1\) ,并且使这些区间满足这些区间内的数之和严格单调递增。请你找出最大的 \(k\)

思路

由于在 \(k\) 不同的情况下从前往后的区间长度不确定,我们可以从后往前考虑 \(DP\) 。对于状态设计,我们可以选择第一维表示到了哪一个元素,第二维表示是第几个区间,而 \(f[i][j]\) 就是从 \(i\) 之后选择 \(j\) 个区间,第 \(j\) 个区间的最大和。可以得出递推式子。

\[f(i,k)=max(f(i+1,k),sum(i,i+k−1)) \]

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10,K=510;
const ll INF=0X3f3f3f3f3f3f3f3f;
int n,a[N],maxk,T;
ll pre[N],f[N][K];
inline void Read()
{
    scanf("%d",&n);
    maxk=sqrt(n+1<<1);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
}
inline void Work()
{
    for(int i=1;i<=n;i++)
        pre[i]=pre[i-1]+a[i];
    for(int i=1;i<=maxk;i++)
        f[n+1][i]=-INF;
    f[n+1][0]=INF;
    for(int i=n;i>=1;i--)
        for(int j=0;j<=maxk;j++)
        {
            f[i][j]=f[i+1][j];
            if(j&&i+j-1<=n&&pre[i+j-1]-pre[i-1]<f[i+j][j-1]) f[i][j]=max(f[i][j],pre[i+j-1]-pre[i-1]);
        }
    for(int i=maxk;i>=1;i--)
        if(f[1][i]>0)
            return (void)printf("%d\n",i);
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        Read();
        Work();
    }
    return 0;
}

F. Korney Korneevich and XOR

『题目链接』

题目难度 \(2400\)(阴间DP)

题目大意

你有一个长度为 \(n\) 的序列,找到序列中递增子序列异或和中出现的值。

思路

由于题目中的值域较小,我们可以考虑从值域出发思考问题。从头到尾扫一遍序列,对于每个值都建立一个带处理数组,其中 \(have_i\) 表示在此元素之前仅用序列前面最后一个元素比他小的子序列和可以组成的所有值,每次拿出 \(have_{a_i}\) 来更新即可。为了增加算法的运行效率,减少不必要的运算,我们可以再增加一个 \(given\) 数组,表示一个值已经在从大到小几个 \(have\) 中出现了,那些中就不要再添加这个元素了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,S=1<<13;
int n,given[N];
vector<int> have[N];
bool got[N];
inline void Read()
{
    scanf("%d",&n);
    for(int i=0;i<S;i++)
    {
        have[i].push_back(0);
        given[i]=S;
    }
}
inline void Work()
{
    got[0]=true;
    for(int i=1;i<=n;i++)
    {
        static int num;
        scanf("%d",&num);
        for(auto x : have[num])
        {
            int to=num^x;
            got[to]=true;
            while(given[to]>num)
                have[given[to]--].push_back(to);
        }
        have[num].clear();
        have[num].shrink_to_fit();
    }
    int ans=0;
    for(int i=0;i<N;i++)
        if(got[i]) ans++;
    printf("%d\n",ans);
    for(int i=0;i<N;i++)
        if(got[i]) printf("%d ",i);
    putchar('\n');
}
int main()
{
    Read();
    Work();
    return 0;
}

G. Kuzya and Homework

『题目链接』

题目难度 \(2600\)(阴间数学题)

题目大意

你有一个序列 \(a\) ,其中都是正整数,还有一个序列 \(b\) ,其中都是 \(/\ ,*\) ,一个操作\((l\ ,\ r)\) ,表示用一个初始值为 \(1\)\(x\) ,从 \(a_l\)\(a_r\) 分别做 \(x\ \oplus \ a_i\)\(\oplus\)\(b_i\) )。请问有多少对 \((l,r)\) ,可以使 \(x\) 在中途任何一个操作都为整数。

思路

我们可以考虑将所有的 \(a_i\) 分解质因数,遇到 \(\times\) 时就将所有的质因数加入对应的栈 。遇到 \(/\) 时就删去栈中的元素,对于每个 \(/\) 操作都可以找到离它最近的“前置操作”(如果质因数不够前置设成 \(-1\) ),这样最后从后往前扫描用单调栈动态更新答案即可。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int n,a[N];
int primes[N],minfac[N],cnt;
int maxl[N];
bool is_nprime[N];
char str[N];
ll ans;
struct node
{
    ll minl,ans;
};
stack<node> stk;
vector<int> pos[N];
inline void Get_prime()
{
    for(int i=2;i<N;i++)
    {
        if(!is_nprime[i])
        {
            primes[++cnt]=i;
            minfac[i]=i;
        }
        for(int j=1;primes[j]*i<N;j++)
        {
            is_nprime[primes[j]*i]=true;
            minfac[primes[j]*i]=primes[j];
            if(i%primes[j]==0) break;
        }
    }
}
inline void Mul(int x,int loc)
{
    maxl[loc]=loc;
    while(x>1)
    {
        pos[minfac[x]].push_back(loc);
        x/=minfac[x];
    }
}
inline void Div(int x,int loc)
{
    maxl[loc]=loc;
    while(x>1)
    {
        if(pos[minfac[x]].size()==0)
        {
            maxl[loc]=-1;
            return;
        }
        maxl[loc]=min(maxl[loc],pos[minfac[x]].back());
        pos[minfac[x]].pop_back();
        x/=minfac[x];
    }
}
inline void Read()
{
    Get_prime();
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    scanf("%s",str+1);
}
inline void Work()
{
    for(int i=1;i<=n;i++)
        if(str[i]=='*') Mul(a[i],i);
        else Div(a[i],i);
    for(int i=n;i>=1;i--)
    {
        ll res=1;
        while(stk.size()&&stk.top().minl>=maxl[i])
        {
            res+=stk.top().ans;
            stk.pop();
        }
        stk.push({maxl[i],res});
        if(maxl[i]==i) ans+=res;
    }
    printf("%lld\n",ans);
}
int main()
{
    Read();
    Work();
    return 0;
}
posted @ 2021-11-10 16:48  Deep_Cold  阅读(126)  评论(0)    收藏  举报