[补档]noip2019集训测试赛(八)

Problem B: 2048

Special Judge

Time Limit: 1000 ms Memory Limit: 256 MB

Description

2048曾经是一款风靡全球的小游戏。

今天,我们换一种方式来玩这个小游戏。

现在,你有一个双端队列,你只能把元素从左端或从右端放入双端队列中。一旦放入就不得取出。放入后,若队列中有连续两个相同的元素,它们将自动合并变成一个新的元素——原来那两个元素的和。若新的元素与它相邻的元素相同,则继续合并……

如:双端队列中有2, 4, 16三个元素。若将2从左端插入双端队列中,该队列将变成8, 16。若将2从右端插入双端队列中,该队列将变成2, 4, 16, 2。

一开始,双端队列为空。我们将给你一些数,你需要依次插入到双端队列中。问是否存在一种操作方案,使得双端队列最后只剩下一个数。

Input

第一行为一个整数 T,表示数据组数。

对于每组测试数据:

第一行一个整数 n,表示我们给你的数的个数。

接下来一行 n个数,表示我们给你的数 ai 。这些数都是2的非负整数次方,即对于每个 i 都存在一个整数 k 满足 k≥0 且 ai=2^k 。

Output

对于每组数据,若不存在一种操作方案使得双端队列最后只剩一个数,则输出no。否则,输出一个长度为n的字符串,其中若第i个数从左边插入到双端队列,则第i个字符为 l ;若第i个数从右边插入到双端队列,则第i个字符为 r 。

Sample Input
3
9
2 8 4 1 1 4 4 4 4
5
2 16 4 8 2
3
2 2 2

Sample Output

rrrlllrrr
no
no

HINT

样例1解释

1、从右边插入2,双端队列变为2

2、从右边插入8,双端队列变为2 8

3、从右边插入4,双端队列变为2 8 4

4、从左边插入1,双端队列变为1 2 8 4

5、从左边插入1,双端队列变为4 8 4

6、从左边插入4,双端队列变为16 4

7、从右边插入4,双端队列变为16 8

8、从右边插入4,双端队列变为16 8 4

9、从右边插入4,双端队列变为32

数据范围与约定

对于20%的数据, n≤19,T≤100

对于所有数据, 1≤n≤1000,a1~an的和不超过2^13,T≤10000,其中 n>20 的数据不超过150组。

Solution

考虑状压+搜索

用一个L表示一下当前状态的左半部分,那么右半部分可以用预处理的前缀和减去L表示出来

如果当前要加入的数比L的lowbit还要小,那么就加入左边

右边部分同理

然后用一个vis表示一下当前状态有没有被搜索过。由于最大只有8192,所以数组是开得下的。

对于某状态的L和R,如果highbit(L)<=highbit(R),那么就把highbit(R)合并到左边,这样左右的合并就完成了

蛮好理解的(虽然我讲得不清不楚)

#include<bits/stdc++.h>
using namespace std;
#define R (sum[x]-L)
#define lowbit(x) x&-x
int n;
int a[10001];
bool flag;
int vis[1010][8195];
bool ans[10001];
int sum[10001];
int highbit[10001];
int sb;
void solve(int x,int L){
    //cout<<x<<" "<<L<<endl;
    if(highbit[L]<=highbit[R])L+=highbit[R];
    if(vis[x][L]==sb)return;
    vis[x][L]=sb;
    if(x==n){
        //cout<<L<<" "<<(lowbit(L))<<endl;
        if(L==(lowbit(L)))flag=true;
        return;
    }
    int xx=x+1;
    int l=(lowbit(L)),r=(lowbit(R));
    if(a[xx]<=l)ans[xx]=0,solve(xx,L+a[xx]);
    if(flag)return;
    if(!r||a[xx]<=r)ans[xx]=1,solve(xx,L);
}
void init(){
    for(int i=2;i<=8192;++i)highbit[i]=highbit[i>>1]+1;
    for(int i=1;i<=8192;++i)highbit[i]=1<<highbit[i];
}
void print(){
    for(int i=1;i<=n;++i){
        putchar((ans[i]?'r':'l'));
    }
    putchar('\n');
}
int main(){
    int T;
    scanf("%d",&T);
    //T=1;
    init();
    while(T--){
        memset(sum,0,sizeof(sum));
        memset(ans,0,sizeof(ans));
        flag=false;
        sb++;
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        //cout<<sum[n]<<endl;
        if(sum[n]!=(lowbit(sum[n]))){
            puts("no");
            continue;
        }
        solve(1,a[1]);
        if(flag){
            print();
        }
        else puts("no");
    }
}

Problem C: Subsequence Count

Time Limit: 1000 ms Memory Limit: 256 MB

Description

给定一个01串 S1?n 和 Q个操作。

操作有两种类型:

1、将 [l,r]区间的数取反(将其中的0变成1,1变成0)。

2、询问字符串 S的子串 Sl?r 有多少个不同的子序列。由于答案可能很大,请将答案对 10^9+7取模。

在数学中,某个序列的子序列是从最初序列通过去除某些元素但不破坏余下元素的相对位置(在前或在后)而形成的新序列。

Input

第一行包含两个整数 N 和 Q,分别表示字符串长度和操作次数。

第二行包含一个字符串 S。

接下来 Q行,每行3个整数 type,l,r ,其中 type 表示操作类型, l,r 表示操作区间为 [l,r] 。

Output

对于每一个 type=2 的询问,输出一个整数表示答案。由于答案可能很大,请将答案对 10^9+7 取模。

Sample Input
4 4
1010
2 1 4
2 2 4
1 2 3
2 1 4

Sample Output

11
6
8

HINT

对于5%的数据, N≤20,Q=1

对于10%的数据, N≤1000,Q=1

对于20%的数据, N≤10^5,Q≤10

对于另外30%的数据, 1≤N≤10^5,1≤Q≤10^5,type=2

对于100%的数据, 1≤N≤105,1≤Q≤105

Solution

求子串个数我们采用dp,设dp[i][0/1],代表dp到第i位,以0/1结尾的子串个数有几个

\(dp[i][s[i]]=dp[i-1][s[i]]+dp[i-1][!s[i]]+1,dp[i][!s[i]]=dp[i-1][!s[i]]\)

然后转成矩阵:

\[
if(s[i]==0)\left[
\begin{matrix}
1 & 0 & 0 \ 1 & 1 & 0 \ dp[i][0] & dp[i][1] & 1 \ \end{matrix}
\right]
\]

\[
if(s[i]==1)\left[
\begin{matrix}
1 & 1 & 0 \ 0 & 1 & 0 \ dp[i][0] & dp[i][1] & 1 \ \end{matrix}
\right]
\]

然后每次查询的话就是一段区间的矩阵乘起来,套个线段树维护一下

如果说是要区间反转0,1的话,直接把矩阵那几个元素交换一下位置好了

我可能写丑了,990ms擦着时限过去,考试的时候被卡常了QAQ

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mod (long long)(1e9+7)
int read(){
    int num=0;
    char ch=getchar();
    while(!isdigit(ch)){
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num;
}
ll write(ll x){
    if(x<0){putchar('-');x=~(x-1);}
    ll s[20],top=0;
    while(x){s[++top]=x%10;x/=10;}
    if(!top)s[++top]=0;
    while(top)putchar(s[top--]+'0');
    putchar('\n');
}
struct matrix{
    ll a[3][3];
    matrix(){a[0][1]=a[0][2]=a[1][0]=a[1][2]=a[2][0]=a[2][1]=0;a[0][0]=a[1][1]=a[2][2]=1;}
};
matrix operator *(matrix a,matrix b){
    matrix c;
    memset(c.a,0,sizeof(c.a));
    for(int i=0;i<3;++i){
        for(int j=0;j<3;++j){
            for(int k=0;k<3;++k){
                c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%mod;
            }
        }
    }
    return c;
}
matrix val[400001];
int tag[400001];
char a[200001];
void build(int o,int l,int r){
    if(l==r){
        if(a[l]=='0'){
            val[o].a[1][0]=val[o].a[2][0]=1;
        }
        else val[o].a[0][1]=val[o].a[2][1]=1;
        return;
    }
    int mid=(l+r)/2;
    build(o*2,l,mid);
    build(o*2+1,mid+1,r);
    val[o]=val[o*2]*val[o*2+1];
}
void swaps(matrix &a){
    swap(a.a[0][0],a.a[0][1]);
    swap(a.a[1][0],a.a[1][1]);
    swap(a.a[2][0],a.a[2][1]);
    swap(a.a[0][0],a.a[1][0]);
    swap(a.a[0][1],a.a[1][1]);
}
void pushdown(int o){
    if(tag[o]){
        swaps(val[o*2]),swaps(val[o*2+1]);
        tag[o*2]^=1,tag[o*2+1]^=1;
        tag[o]=0;
    }
}
void update(int o,int l,int r,int L,int R){
    if(L<=l&&r<=R){
        tag[o]^=1;
        swaps(val[o]);
        return;
    }
    pushdown(o);
    int mid=(l+r)/2;
    if(L<=mid)update(o*2,l,mid,L,R);
    if(mid<R)update(o*2+1,mid+1,r,L,R);
    val[o]=val[o*2]*val[o*2+1];
}
matrix query(int o,int l,int r,int L,int R){
    if(L<=l&&r<=R){
        return val[o];
    }
    pushdown(o);
    int mid=(l+r)/2;
    matrix ret;
    if(L<=mid)ret=ret*query(o*2,l,mid,L,R);
    if(mid<R)ret=ret*query(o*2+1,mid+1,r,L,R);
    return ret;
}
int main(){
    int n,m;
    scanf("%d%d%s",&n,&m,a+1);
    build(1,1,n);
    while(m--){
        int opt=read(),l=read(),r=read();
        if(opt==1){
            update(1,1,n,l,r);
        }
        else {
            matrix tmp=query(1,1,n,l,r);
            write((tmp.a[2][1]+tmp.a[2][0])%mod);
        }
    }
}

原文地址:https://www.cnblogs.com/youddjxd/p/11442067.html

时间: 2024-10-13 05:16:55

[补档]noip2019集训测试赛(八)的相关文章

[补档]noip2019集训测试赛(十五)

Problem A: 传送带 Time Limit: 1000 ms Memory Limit: 256 MB Description 在一个二维平面上有两条传送带,每一条传送带可以看成是一条线段.两条传送带分别为线段AB和线段CD.小y在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移动速度R.现在,小y想从A点走到D点,请问他最少需要走多长时间. Input 第一行是4个整数,表示A和B的坐标,分别为Ax,Ay,Bx,By. 第二行是4个整数,表示C和D的坐标,分别为Cx,Cy,D

[补档]noip2019集训测试赛(十二)

Problem A: 记忆(memory) Time Limit: 1000 ms Memory Limit: 512 MB Description 你在跟朋友玩一个记忆游戏. 朋友首先给你看了n个长度相同的串,然后从中等概率随机选择了一个串. 每一轮你可以询问一个位置上的正确字符,如果能够凭借已有的信息确定出朋友所选的串,那么游戏就结束了,你的成绩就是所用的轮数. 由于你实在太笨,不会任何策略,因此你采用一种方法,每次等概率随机询问一个未询问过的位置的字符. 现在你想知道,在这种情况下,你猜出

noip2019集训测试赛(二)

Problem A: 余数 Time Limit: 1000 ms Memory Limit: 512 MB Description Input Output Sample Input 3 4 Sample Output 4 HINT Solution 那个所谓\(\sqrt n\)的东西叫做整除分块. 显然对于\(n÷i\),当$i<=\sqrt n $时,每个i都有一种取法 当\(n>=i>\sqrt n\),i每加\(\sqrt n\),\(n÷i\)的值就+1 然后就可以根号时间

noip2019集训测试赛(四)

Problem A: fibonacci Time Limit: 3000 ms Memory Limit: 256 MB Description Input 第一行两个数 N,M . 第二行 N 个数 a1,a2,...,an . 接下来 M 行, 每行代表题目描述中的一种操作. Output 对于每个询问, 输出一行, 表示答案. Sample Input 5 4 1 1 2 1 1 2 1 5 1 2 4 2 2 2 4 2 1 5 Sample Output 5 7 9 HINT 对于

noip2019集训测试赛(七)

Problem A: Maze Time Limit: 1000 ms Memory Limit: 256 MB Description 考虑一个N×M的网格,每个网格要么是空的,要么是障碍物.整个网格四周都是墙壁(即第1行和第n行,第1列和第m列都是墙壁),墙壁有且仅有两处开口,分别代表起点和终点.起点总是在网格左边,终点总是在网格右边.你只能朝4个方向移动:上下左右.数据保证从起点到终点至少有一条路径. 从起点到终点可能有很多条路径,请找出有多少个网格是所有路径的必经网格. Input 第一

【2016北京集训测试赛(八)】 crash的数列

Description 题解 题目说这是一个具有神奇特性的数列!这句话是非常有用的因为我们发现,如果套着这个数列的定义再从原数列引出一个新数列,它居然还是一样的...... 于是我们就想到了能不能用多点数列套着来加速转移呢? 但是发现好像太多数列套起来是可以烦死人的...... 我们就采用嵌套两次吧,记原数列为A,第一层嵌套为B,第二层嵌套为C. 我们其实可以发现一些规律,对于Ci,它对应了B中i的个数:对于Bi,它对应了A中i的个数. 稍加处理即可,我们一边计算一边模拟数列的运算,同时可以计算

2016集训测试赛(二十五)小结

这场测试赛有必要小结一下. 昨晚 1 点才睡, 今天状态很差, 先睡了 1 个小时, 然后开始打比赛. 第一题不大会做, 写了一个代码后发现是错的, 第二题看不懂题, 第三题简单地分析了一下, 发现是一个树形DP . 然后做 T3 , 大概推了很久, 写了很久, 又写了几个对拍, 搞到 11 点才搞掂. 这时候我发现 T1 有 50 分是我可做的, 然后 T2 的题意仍然不是很明确, 我想尝试着写写 T2 , 这个必须要写出来才能看出题意是不是这样, 写着写着发现 T2 的题意不是我理解的这样,

noip2017集训测试赛(三) Problem B: mex [补档]

Description 给你一个无限长的数组,初始的时候都为0,有3种操作: 操作1是把给定区间[l,r][l,r] 设为1, 操作2是把给定区间[l,r][l,r] 设为0, 操作3把给定区间[l,r][l,r] 0,1反转. 一共n个操作,每次操作后要输出最小位置的0. Input 第一行一个整数n,表示有n个操作 接下来n行,每行3个整数op,l,r表示一个操作 Output 共n行,一行一个整数表示答案 Sample Input 3 1 3 4 3 1 6 2 1 3 Sample Ou

【2016北京集训测试赛(八)】直径

注意:时限更改为4s 题解 考虑最原始的直径求法:找到离根节点(或离其他任意一点)最远的节点far1,再从far1出发找到离far1最远的节点far2,far1至far2的距离即为直径. 题目中提到了将原树的子树复制成新子树这一操作,显然如果我们将子树真正复制出来是会爆炸的. 实际上我们可以将每棵新子树中,真正有用的节点提取出来,以简化每个子树的结构,再按照题目的要求连接各个新子树. 我们用虚树来重构每一棵子树.每棵子树的虚树的关键点应至少包含: 子树的根节点. 这棵子树内部的直径的两端节点.