2020牛客寒假算法基础集训营第三场

牛牛的迷宫2
思路:首先我们可以很模糊的联想到如果按照一下递归下去
BD
RBD
RBD
RB
会发现每个B沿着下去会像二进制一样,1 2 4 8....,所以我们发现只要我们的矩阵的对角线按这种模式建找就行;
因为二进制是可以表示所有数字的,所以我们只需在每一行建造三个B,然后左下角全为D,
最后一行R表示向右到终点,所以,只要这个K的数字的二进制这个位为0,那么我们对应得这一行得第一个B给改为R
不给“流下来”到终点。

2020牛客寒假算法基础集训营第三场_第1张图片

 

 

#include
using namespace std;
const int mod=1e9+7;
int i,i0,n,m,ans,k;
char mp[55][55];
int main()
{
    n=32,m=30;
    scanf("%d",&k);
    mp[n][m]='B';
    for(int i0=1;i0<=m;i0++)mp[1][i0]=(i0<=2)?'B':'R';
    for(int i=2;i<=n-1;i++)
    {
        for(int i0=1;i0<=m;i0++)
        {
            if(i0 
 

  F

静态区间和

F
第一种:
#include
using namespace std;
const int MAXN=100005;
const long long mod=1e9+7;
char s[MAXN];
int n;
long long sum,cnt,ans;
int main()
{
    scanf("%d",&n);
    scanf("%s",s+1);
    for(int i=1;i<=n;++i)
    {
        sum=(sum+cnt)%mod;
        if(s[i]=='1')
        {
            ans=(ans+sum)%mod;
            ++cnt;
        }
    }
    printf("%lld\n",ans);
    return 0;
}
第二种,静态维护区间和
https://blog.nowcoder.net/n/1c3504ca9e3f40d2a37376cd1b76ddec
然后我们发现我们这道题差不多,每个点上的1对后面的点的贡献是1 2 3 4 5 6 7 8 9.....然后我们ans就是遇到一个点为1
就加上这个点上的贡献。
那和我们上面那个链接里的有什么关系呢,你会发现每个点的贡献就是一个等差序列,这就符合上面那个了
然后我们直接改改
#include
using namespace std;
const int MAXN=100005;
const long long mod=1e9+7;
char s[MAXN];
int n;
long long ans,sum[MAXN];
void pre_sum()
{
    for(int i=1;s[i];++i)
    {
        sum[i]+=sum[i-1];
        if(sum[i]>=mod)sum[i]-=mod;
    }
}
int main()
{
    scanf("%d",&n);
    scanf("%s",s);
    for(int i=0;s[i];++i)
    {
        if(s[i]=='1')
        {
            sum[i+1]++;
        }
    }
    pre_sum();
    pre_sum();
    for(int i=1;s[i];++i)
    {
        if(s[i]=='1')
        {
            ans+=sum[i];
            if(ans>=mod)ans-=mod;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

  动态修改区间和(线段树走起

#include 
#include 
#include 
///线段树,维护区间内1的数量cnt、区间的Link值w、区间内所有的
///1到区间左边界的距离之和dl、区间内所有的1到区间右边界的距离
///之和dr,查询时只要输出tree[1].w即可,修改时只用改变区间内
///1的cnt,然后进行区间合并,区间合并的公式画个图推导一下即可
using namespace std;

typedef long long ll;

const int N = 100010;
const ll mod = 1000000007;

struct node {
    int l, r;
    ll dl, dr, w, cnt;
};

node tree[4 * N];
char s[N];
int n, q;

void update(int k)
{
    tree[k].cnt = tree[2 * k].cnt + tree[2 * k + 1].cnt;
    ll t = tree[2 * k].cnt * tree[2 * k + 1].dl + tree[2 * k + 1].cnt * tree[2 * k].dr;
    tree[k].w = tree[2 * k].w + tree[2 * k + 1].w + t + tree[2 * k].cnt * tree[2 * k + 1].cnt;
    tree[k].dl = tree[2 * k].dl + tree[2 * k + 1].dl + tree[2 * k + 1].cnt * (tree[2 * k].r - tree[2 * k].l + 1);
    tree[k].dr = tree[2 * k].dr + tree[2 * k + 1].dr + tree[2 * k].cnt * (tree[2 * k + 1].r - tree[2 * k + 1].l + 1);
}
void build(int k, int lef, int rig)
{
    tree[k].l = lef, tree[k].r = rig;
    if (tree[k].l == tree[k].r) {
        tree[k].w = tree[k].dl = tree[k].dr = 0;
        if ('1' == s[tree[k].l]) tree[k].cnt = 1;
        else tree[k].cnt = 0;
        return;
    }
    int mid = (lef + rig) / 2;
    build(k * 2, lef, mid);
    build(k * 2 + 1, mid + 1, rig);
    update(k);
}

void change_point(int k, int x, int y)
{
    if (tree[k].l == tree[k].r) {
        tree[k].cnt = y;
        return;
    }
    int mid = (tree[k].l + tree[k].r) / 2;
    if (x <= mid) change_point(2 * k, x, y);
    else change_point(2 * k + 1, x, y);
    update(k);
}

int main()
{
    scanf("%d%s", &n, s + 1);
    build(1, 1, n);
    printf("%lld\n", tree[1].w % mod);
    scanf("%d", &q);
    while(q--){
        int kd, pos;
        scanf("%d%d", &kd, &pos);
        change_point(1, pos, 1 == kd ? 1 : 0);
        printf("%lld\n", tree[1].w % mod);
    }
    return 0;
}  

求在1~n内,询问含有恰好k给合数因子的数有几个

埃式筛筛出质数,然后对于合数再筛一遍,然后统计每个数字被筛到的次数。桶排序一下即可。
#include
using namespace std;
const int MAXN=100005;
int f[MAXN],cnt[MAXN],n,m,x;
bool prim[MAXN];
void init(int n)
{
    for(int i=2;i<=n;++i)
    {
        prim[i]=true;
    }
    for(int i=2;i<=n;++i)
    {
        if(prim[i])
        {
            for(int j=i+i;j<=n;j+=i)
            {
                prim[j]=false;
            }
        }
        else
        {
            for(int j=i;j<=n;j+=i)
            {
                cnt[j]++;
            }
        }
        f[cnt[i]]++;
    }
}
int main()
{
    scanf("%d %d",&n,&m);
    init(n);
    while(m--)
    {
        scanf("%d",&x);
        printf("%d\n",f[x]);
    }
}

  

题目大意入上图所述

具体思路实现:
首先,考虑问题的解法,因为任意时刻都不相同(没有同一个时刻刷出两个怪及以上的可能),那么令:

dp[i]代表到第i个时间点能获得的最大权值dp[i]i

那么这个题的状态转移方程其实也很好推的:
首先按照时间排序, 然后去找一下之前的时间点,能不能与这个时间点相连接,也就是说,假设前面有两个时间点
1 2 ,我考虑由1转移过来,还是由2转移过来,那么转移过来的条件就是,那个时间点的位置+那个时间点的时间到当前点的时间(k)<=当前时间点的时间(那就可以赶过来得到该权值)。

这时候 那个时间点的时间到当前点的时间 我们考虑最优状态转移,那么一定是k->i的最短路,所以我们要预处理一遍最短路。

所以m^2的算法也就显而易见,这时候去考虑如何优化状态,看一下 那个时间点的位置+那个时间点的时间到当前点的时间(k) 这个式子用符号化语言来表述的话就是:dis[i][k]+save[k].t<=save[i].tdis[i][k]+save[k].t<=save[i].t
这里其实dis[i][k]与dis[k][i]是相等的,因为图无向
考虑dis[i][k]的最大值 -> n ,因为全图只有n个点而且图联通。再基于没有时间点重复,也就说 i与i-n之前的所有的时间点 至少相差n,所以说i-n之前的可以不用去比较上述判断条件,直接转移,所以我们新开一个数组保留前缀最小,直接转移就可以了 ,复杂度 O(M*N) ~2e7

(意思就是我们每次dp【i】都是从1到i-1时间dp过去的,当这个i越来越大,那这样肯定超时,我们发现整个图就n个点,所以我们只需对于每个i枚举(i-n)~(i)就行,那1~1-n的呢,因为他们和i的差大于n,所以是不用进行上面的dp里的比较,

因为dp的比较是能不能够时间到达这个i点,但是我们差大于n的,所以我们是不用担心够不够时间的。

这其中有个坑,因为初始点是1,所以说起初的状态有可能从1过不来,所以dp数组的初始化不可以设置为-1,当然你可以令开一个bool数组标记当前时间点能否到达,能到达才进行转移,如果不开数组标记的话,将时间点设为-1e18(最小值),这样的话,只要不能到达的再怎么加也不可能使得状态转移。最后遍历得最大值得时候,将ans初始化为0,就可以略去负数情况(负数即为不可到达).

#include 
#include
#include
#include
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const ll INF=1000000000000007;
const ll maxn=1e6+5;
const int mod=1e9+7;
ll n,m,p;
ll mp[205][205];
struct node{
    ll p,w,t;
    bool operator<(const node&x)const{
        return tn) dp[i]=max(dp[i],cop[i-n]+save[i].w);
        for(int k=1;k<=n&&i-k>=0;k++){
            if(save[i-k].t+mp[save[i-k].p][save[i].p]<=save[i].t)
                dp[i]=max(dp[i],dp[i-k]+save[i].w);
        }
        ans=max(dp[i],ans);
        cop[i]=max(cop[i-1],dp[i]);
      //  printf("%lld ",dp[i]);
    }
    printf("%lld\n",ans);
    return 0;
}

  

你可能感兴趣的:(2020牛客寒假算法基础集训营第三场)