cf训练2

CF 616 div1

A

题解

\(k \geq m-1\) ,我们可以任意指定顺序,我们求每个方案的最大值即可。

\(k < m-1\) ,发现我们有 \(m - k\) 种可能,取最小值即可。

#include
#include
#include
#include
#define ll long long
using namespace std;
int read()
{
    int k=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) k=k*10+c-'0';return k*f;
}
const int N=5005;
int n,m,k,T,a[N];
int main()
{
    for(T=read();T;T--)
    {
        n=read();m=read();k=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        if(k>=m-1)
        {
            int ans=0;
            for(int i=1;i<=m;i++)   
                ans=max(ans,max(a[i],a[n-m+i]));
            printf("%d\n",ans);
            continue;
        }
        else
        {
            int ans=0,x=m-1-k;
            for(int i=0;i<=k;i++)
            {
                int as=0x7fffffff;
                for(int j=i+1;j<=i+x+1;j++)
                    as=min(as,max(a[j],a[n-m+j]));
//              cout<

B

题解

我们发现如果字符串最左和最右的字符不同,那么一定可以有 irreducible anagram ,我们把首尾换一下就行。 如果相同,且字符种类大于等于三,我们找到最后一个与末尾字符不同的字符 $s[k] \neq s[n] $ ,把 \(s[k]\) 的所有字符放在最前面,再放 \(s[n]\) 的所有字符,最后把剩余字符随意摆放,这样就的串就是Irreducible Anagrams 。

当字符种类小于三时,显然不存在 Irreducible Anagrams。

统计一下每个字符的前缀和就可以了。

#include
#include
#include
#include
#define ll long long
using namespace std;
int read()
{
    int k=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) k=k*10+c-'0';return k*f;
}
const int N=200055;
int n,a,b,sum[N][26];
char s[N];
int main()
{
    scanf("%s",s+1);
    n=strlen(s+1);
    for(int i=1;i<=n;i++)
    {
        memcpy(sum[i],sum[i-1],sizeof(sum[i]));
        sum[i][s[i]-'a']++;
    }
    n=read();
    for(int i=1;i<=n;i++)
    {
        a=read();b=read();
        if(a==b) puts("Yes");
        else if(s[a]!=s[b]) puts("Yes");
        else 
        {
            int cnt=0;
            for(int j=0;j<26;j++)
                if(sum[b][j]-sum[a-1][j])
                    cnt++;
            if(cnt>2) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

C

题解

首先,每一盏灯最多在两个集合里,如果在一个或不在集合里,对该灯的操作是确定的。

接下来重点讨论在两个集合的做法,发现,不管灯是亮的还是灭的,我们都只有两种操作方法,亮的:一个操作一个不操作,灭的:都操作或都不操作。

我们可以用并查集维护集合,\(1 - m\) 表示对集合操作,\(m+1 - 2m\) 表示对集合不操作,我们按顺序开灯,同时处理并查集的关系并计算答案。 细节见代码。

#include
#include
#include
#include
#include
#define ll long long
using namespace std;
int read()
{
    int k=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) k=k*10+c-'0';return k*f;
}
const int N=600055;
int n,m,a[N],fa[N],size[N],c[N][3],fl[N];
char s[N];
int find(int x)
{
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}
int get(int x)
{
    int a=find(x),b=find(x+m);
    if(fl[a]) return size[a];
    if(fl[b]) return size[b];
    return min(size[a],size[b]);
}
int main()
{
    n=read();m=read();
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
        a[i]=s[i]-'0';
    for(int i=1;i<=m;i++)
    {
        int x=read();
        for(int j=1;j<=x;j++)
        {
            int y=read();
            c[y][++c[y][0]]=i;
        }
    }
    int now=0;
    for(int i=1;i<=m*2;i++)
        fa[i]=i,size[i]=(i<=m);
    for(int i=1;i<=n;i++)
    {
        if(c[i][0]==1)
        {
            now-=get(c[i][1]);
            if(a[i]) fl[find(c[i][1]+m)]=1;
            else fl[find(c[i][1])]=1;
            now+=get(c[i][1]);
        }
        else if(c[i][0]==2)
        {
            int x=find(c[i][1]),y=find(c[i][2]),z=find(c[i][1]+m),w=find(c[i][2]+m);
            if(!a[i]&&x!=w)
            {
                now-=get(c[i][1])+get(c[i][2]);
                if(w!=x) size[w]+=size[x];fl[w]|=fl[x];fa[x]=w;
                if(z!=y) size[z]+=size[y];fl[z]|=fl[y];fa[y]=z;
                now+=get(c[i][1]);
            }
            else if(a[i]&&x!=y)
            {
                now-=get(c[i][1])+get(c[i][2]);
                if(x!=y) size[y]+=size[x],fl[y]|=fl[x];fa[x]=y;
                if(z!=w) size[z]+=size[w],fl[z]|=fl[w];fa[w]=z;
                now+=get(c[i][1]);
            }
        }
        printf("%d\n",now);
    }
    return 0;
}

D

题解

\(k=1\) 时,我们逐个对比就行。

$ k \neq 1$ 时,我们把咖啡按 \(k/2\) 的大小分组,逐组比较,把记得的咖啡打上标记,最后统计没有标记的数目就是答案,这样就可以做到 $ \frac{2n^2} {k} $ 。

继续优化,我们发现先尝第一组,第二组,再尝第三组。相当于对比了一二组和二三组。不用清空记忆。那么我们把上面的方案改变顺序,连续品尝即可。先 $ 1\quad 2 \quad 3 ... n$ ,相当于问了$ 1,2 \quad 2,3 \quad 3,4\quad ...n-1,n$ ,再 $ 1 \quad 3 \quad ....n-1$ ,$ 2 \quad 4 \quad .... n$ ,每次问完一大组再清空。

询问次数 $ \frac{3n^2} {2k} $ 。

#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
int read() {
    int k=0,f=1;
    char c=getchar();
    for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;
    for(; isdigit(c); c=getchar()) k=k*10+c-'0';
    return k*f;
}
const int N=1550;
int n,m,cnt[N];
char s;
vector V;
int main() {
    cin>>n>>m;
    fflush(stdout);
    if(m==1) {
        int now=0;
        for(now=1; now<=m+1&&now<=n; now++) {
            cout<<"? "<>s;
            if(s=='N') V.push_back(now);
        }
        cout<<"R\n";
        for(int i=now; i<=n; i++) {
            cout<<"? "<>s;
            int fl=1,now=0;
            for(int j=0; j>s;
                if(s=='Y') {
                    fl=0;
                    break;
                }
                if(now==m) {
                    cout<<"R\n";
                    fflush(stdout);
                    cout<<"? "<>s;
                    now=0;
                }
            }
            if(fl) V.push_back(i);
            cout<<"R\n";
            fflush(stdout);
        }
        cout<<"! "<>s;
                        if(s=='Y') cnt[p]=1;
                    }
            }
        int ans=0;
        for(int i=1; i<=n; i++)
            if(!cnt[i]) ans++;
        cout<<"! "<

你可能感兴趣的:(cf训练2)