Educational Codeforces Round 74 (19/11/3)

A - Prime Subtraction

给定 n ,m(1<=n,m<=1e18) ,且 n > m,询问 n - m 是否是一个质数的倍数。
显然由质因数分解可知,只要数>=2都可以,直接判n-m==1即可。

代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
#define maxm 1006
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (x*x)
#define inf (ll)2e18+1
#define PI acos(-1)
#define mod 1000000007
#define auto(i,x) for(int i=head[x];i;i=ed[i].nxt)
ll read(){
    ll x=0,f=1ll;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
     while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
     return f*x;
}
int T;
ll n,m;
int main()
{
    T=read();
    while(T--){
        n=read();m=read();
        if(n-m==1)printf("NO\n");
        else printf("YES\n");
    }
    return 0;
}
B - Kill `Em All

有 n 个怪物,他们的初始坐标在一个一维直线上,为 xi,其中 1<=xi<=1e9,在坐标小于等于0的部分可以理解为一个大的陷阱,怪物掉入其中就会暴毙,现在你可以选择任意位置放置炸弹,其效果是,在炸弹放置位 y 上的所有怪物当场暴毙(可能同一个点有多个),对于不在 y 点的怪物,如果在其左侧 则会被左移 r 个单位,如果在其右侧则会被右移 r 个单位,即 xi = xi -r 或者 xi = xi + r。
如果位移后 xi <= 0 ,算做掉入陷阱暴毙。询问最少放置多少个炸弹可以使得所有的怪都暴毙。

由于如果不在炸弹点 y 点会整体位移,容易想到像右边位移的话不会使得所有的怪物的间隔改变,所以还是要一个一个炸,所以贪心的选择右边的怪,将所有怪都向左移动,这样会存在怪物掉入陷阱致死,而向右位移则还是要一个个炸。
由于同一个点可能有多个怪,记得离散化。

代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
#define maxm 1006
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (x*x)
#define inf (ll)2e18+1
#define PI acos(-1)
#define mod 1000000007
#define auto(i,x) for(int i=head[x];i;i=ed[i].nxt)
ll read(){
    ll x=0,f=1ll;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
     while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
     return f*x;
}
int T,n,m,a[maxn];
int main()
{
    T=read();
    while(T--){
        n=read();m=read();
        inc(i,1,n)a[i]=read();
        sort(a+1,a+n+1);
        int len=unique(a+1,a+n+1)-(a+1);
        int l=1,r=len;
        ll sum=0;
        while(l<=r){
            while(a[l]<=sum)l++;
            if(l>r)break;
            sum+=m;
            r--;
        }
        printf("%lld\n",sum/m);
    }
    return 0;
}
C - Standard Free2play

题意比较长好像,给一个高度为 h 的山崖 (1<= h <= 1e9),每个单位高度的地方有个隔板(人站在上面),初始时人站在 高度为 h 的隔板上,给定 m 个位置隔板的状态为 伸展,其中一定包含了高度为 h 的位置,现规定下降的规则为,当你在 高度为 h 的隔板上时,他一定是伸展的,然后你可以按一个神奇的按钮,使得当前隔板和下一个隔板的状态转变,就是伸展变收缩,收缩变伸展,假设 1 为伸展,0 为收缩,那么你当前 h 的状态一定为 1 (不然咋站) ,如果h -1为 0,那么状态转变后 h 变 0,h-1 变 1 ,你刚好掉落到 h-1 上,同时规定 最多下落2个单位高度,即 h 最多直接掉到 h-2,否则会摔死。问你最多转变多少个位置的初始状态,可以从 h 安全降落到 0。

一开始我打算拿数组记录每个位置的状态的 ,结果 h 这么大,后来发现,如果一个高度 h 位置是 1 下面位置都是 0 ,那么这个1是可以直接安全传递下去的,所以无论两个1 的高度差多少,最终也就是上面一个1 转移到了 下面一个1 的上面一个单位,所以所有可能出现问题的状态就是两个1叠在一起的时候,需要判断这两个1同时变0之后,下面有没有1可以接住人,且不至于摔死,否则的话,就要把下面一个1的状态改变,让上面的1 继续安全传递。但是为什么不选择在下面补一个1,而是去掉第二个1呢,因为如果你补一个1,就要紧跟着这两个1补,也就是变成111,那么实际上也就是相当于向下移动了两个,跟100向下移动两步是一样的,所以两种理解都可。然后就是代码实现。

代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 200005
#define maxm 1006
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (x*x)
#define inf (ll)2e18+1
#define PI acos(-1)
#define mod 1000000007
#define auto(i,x) for(int i=head[x];i;i=ed[i].nxt)
ll read(){
    ll x=0,f=1ll;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
     while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
     return f*x;
}
int T,n,m,a[maxn],dp[maxn][2];
int main()
{
    T=read();
    while(T--){
        n=read();m=read();
        inc(i,1,m)a[i]=read();
        sort(a+1,a+m+1);
        int ans=0,now=n;
        for(int i=m-1;i>=1;i--){
            if(now<=a[i])continue;
            if(a[i-1]<a[i]-1)ans++;
            else now=a[i-1];
        }
        printf("%d\n",ans);
    }
    return 0;
}
D - AB-string

给一个长度为 N 的字符串,只包含A,B两种字符,然后定义了一种好的串是这样的,长度大于等于2,且是一个或多个回文串拼起来的。询问这个长度为 N 的字符串有多少个字串是好的串。

一个很精髓的地方是,因为只有两种字母,所以写写看容易发现,基本上都满足是好的串,除了整个一段全是 一种字母 然后两边有一个另一种字母,例如 AAAB BBBA BAA,这种…
于是考虑反过来求解不满足条件的串的个数,只要在原串中找长度大于等于2的连续相同字母串,然后统计即可,两端的特殊处理注意一下。还有就是我写的时候为了方便把长度为2的也直接剪掉了,之后再从连续串里面加,主要是为了防止 ABAB,这种长度为2的重复统计两端的,也就是 ABA 和 BAB 这两个BA,所以才只记录长度大于等于2的。这个有很多写法可以避免,大家各自写吧。

代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 300005
#define maxm 1006
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (x*x)
#define inf (ll)2e18+1
#define PI acos(-1)
#define mod 1000000007
#define auto(i,x) for(int i=head[x];i;i=ed[i].nxt)
ll read(){
    ll x=0,f=1ll;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
     while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
     return f*x;
}
int n;
char s[maxn];
int main()
{
    n=read();
    scanf("%s",s+1);
    ll ans=1ll*(n+1)*n/2 - n - (n-1);
    int st=1,ed=1;
    inc(i,2,n){
        if(s[i]==s[i-1])ed=i;
        else {
            if(ed-st+1>=2){
                if(st==1)ans-=ed-st+1-1;
                else ans-=2*(ed-st);
                ans+=ed-st;
            }
            st=ed=i;
        }
    }
    if(ed-st+1>=2&&st!=1)ans-=ed-st;
    if(ed-st+1>=2)ans+=ed-st;
    printf("%lld\n",ans);
    return 0;
}
E - Keyboard Purchase (神奇状压)

有一个键盘它是长条形的,是一个前 m 个小写字母的排列,有个憨憨敲代码只会一个手指敲,现给出他要按顺序敲的字符串,只包含前m个字母,长度为 n。(1<=n<=100000,1<=m<=20)每次敲完一个字母,到下一个字母之间在键盘是有一段间隔,现在这个键盘的排列未知,要求一个键盘使得敲完这段字符串最少的移动距离是多少。

m 比较小考虑状压,(不看题解我哪想得到是状压),状态 i 表示目前已知顺序的字母,初始的时候想成所有字母的距离为0,假设 状态 i 中有 x 个1 ,现在跟新状态 i 可以理解成,x-1个 1 的顺序已知,那么再插进来一个字母放置 前 x-1个后面,那么带来的新的距离贡献是,后面的所有不在 i 里面的字母距离前面 x-1个字母的距离都增加了1,包括加进来的这个字母本身,原来看作和剩下的字母0距离,现在放在i的末尾,也带来了1 的距离贡献(因为剩下的字母还没排队,看作一体的)转移方式描述完毕。

由上述所说,暴力枚举 两个字母是否在 i 内算贡献,复杂度 mm2^m,由于cf跑的很快所以可以过,但实际上可以优化到 m*2 ^m。以后再填坑吧。

代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
#define maxm 1006
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (x*x)
#define inf (ll)2e18+1
#define PI acos(-1)
#define mod 10007
#define auto(i,x) for(int i=head[x];i;i=ed[i].nxt)
ll read(){
    ll x=0,f=1ll;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
     while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
     return f*x;
}
int n,m;
char s[maxn];
int dp[1<<20],vis[20][20];
int main()
{
    n=read();m=read();
    scanf("%s",s+1);
    inc(i,2,n)vis[s[i]-'a'][s[i-1]-'a']++,vis[s[i-1]-'a'][s[i]-'a']++;
    for(int i=1;i<(1<<m);i++){
        dp[i]=INF;
        int c=0;
        inc(j,0,m-1)inc(k,j+1,m-1)if(((i>>j)&1)^((i>>k)&1))c+=vis[j][k];
        inc(j,0,m-1)if((i>>j)&1)dp[i]=min(dp[i],dp[i^(1<<j)]+c);
    }
    printf("%d\n",dp[(1<<m)-1]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值