牛客寒假算法基础集训营5 非官方题解

附官方题解链接


A 炫酷双截棍

链接:https://ac.nowcoder.com/acm/contest/331/A
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
Special Judge, 64bit IO Format: %lld

题目描述
小希现在手里有一个连着的两块木条,长度分别为 l 1 , l 2 l_1,l_2 l1,l2,木条之间有一个无摩擦的连接点,木条之间可以相互转动,小希将其称之为双截棍。
现在小希把长为 l 1 l_1 l1的木条的一端放在原点 ( 0 , 0 ) (0,0) (0,0),任意转动这两根木条,小希想知道,是否有可能通过一种转动方式使得双截棍的另一端到达指定点呢?
如果不能,请输出所有能到达的点中离目标点最近的距离。

输入描述:
第一行输入一个两个正整数 l 1 , l 2 l_1,l_2 l1,l2,表示木条长度。
第二行输入一个正整数T,表示询问次数。
随后T行,每行两个实数 x i , y i x_i,y_i xi,yi表示目标点的坐标。

l 1 , l 2 ≤ 1000 l1,l2≤1000 l1,l21000
T ≤ 1000 T≤1000 T1000
∣ x ∣ , ∣ y ∣ ≤ 10000 |x|,|y|≤10000 x,y10000

输出描述:
对于每次询问,如果可以到达,输出0,如果无法到达,给出所有能到达的点中离目标点最近的距离。
你的答案将被认为是正确的,如果相对误差不大于1e-6。

示例1

输入
23 13
3
15 1
40 0
0 0

输出
0.00000000
4.00000000
10.00000000


把两根木棍看成首尾相接的两个向量,根据向量的知识,两向量之和的长度最小值为其长度的差的绝对值,最大值为其长度的和的绝对值,所以在这个范围内就输出0
在这个范围外,就输出 m i n ( ∣ d − l m i n ∣ , ∣ l m a x − d ∣ ) min(|d-lmin|, |lmax-d|) min(dlmin,lmaxd)
基本的数学几何问题
一点点坑点在x和y是实数,要开double,我就因为开int而wa了一发

#include<bits/stdc++.h>
using namespace std;
int main() {
    int l1, l2; scanf("%d%d", &l1, &l2);
    int lmax = l1+l2, lmin = abs(l1-l2);
    int T; scanf("%d", &T);
    while(T--) {
        double x, y; scanf("%lf%lf", &x, &y);
        double d = sqrt(x*x + y*y);
        if(lmin <= d && d <= lmax) printf("0.0000000000\n");
        else printf("%.10lf\n", min(abs(d-lmin), abs(lmax-d)));
    }
    return 0;
}

B 炫酷五子棋

链接:https://ac.nowcoder.com/acm/contest/331/B
来源:牛客网

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述
五子棋是一个简单的双人游戏。
小希最近在思索一种更好玩的五子棋。她希望胜利不再是谁先五子连珠谁赢,而变成谁落子后,该子与之前的子五子连珠的次数更多才能胜利。
但是如果是在普通的棋盘上,这个游戏又显得不是很有趣,所以她将棋盘扩大至N*N,因为棋盘过大,没有一个程序能将其展示出来,所以如何落子只能凭借记忆。
她希望你能写一个程序,判断每步落子与之前的同色棋子是否能形成五子连珠。
五子连珠是指是横着竖着或者斜着的八个方向存在连续的颜色相同的至少五个子。
注意:这个版本的五子棋仍然是双人游戏,先手执黑,后手执白。同色才是五子棋。

输入描述:
第一行一个正整数N,M,表示棋盘大小和落子次数。
随后M行,每行两个整数 x i , y i x_i,y_i xi,yi,表示落子位置。

N , M ≤ 300 , 000 N,M≤300,000 N,M300,000
1 ≤ x i , y i ≤ N 1≤xi,yi≤N 1xi,yiN
数据保证同一个子的位置不会多次落子。

输出描述:
对于每一个子,一行,如果该步落下后,该子和其他子能形成五子连珠,输出一个大写的’Y’,否则输出一个大写的’N’。

示例1

输入
6 12
1 1
6 1
2 2
5 2
3 3
4 3
4 4
3 4
5 5
2 5
6 6
1 6

输出
N
N
N
N
N
N
N
N
Y
Y
Y
Y


嗯这题居然是暴力过的???
因为n比较大,不能直接对棋盘开出数组来,故要用一个数据结构来存储已经落子的信息
每次落子进行查询时,基本操作为询问一个坐标是否落有相同颜色的棋子,所以上面的数据结构要支持这个查询操作
emmmm基本想到用set了
m次行棋,故有m次更新操作,单次更新操作的复杂度为 O ( l o g m ) O(logm) O(logm),之后为查询是否有五子相连的情况,要对周围的棋子进行若干次询问,可以知道这个询问次数为常数(最多询问8个方向,每个方向最多4次询问)。单词询问的复杂度为 O ( l o g m ) O(logm) O(logm),故总的复杂度为 O ( m l o g m ) O(mlogm) O(mlogm),既然m在3e5这个量级,常数小一点是能过的。
嗯我TLE了三发,想想就多啰嗦一下我怎么T的,不想看我啰嗦的话可以直接翻到最下面那份代码。那是AC的
第一发TLE:

#include<bits/stdc++.h>
using namespace std;
struct Node {
    int x, y, c;
    Node(int x, int y, int c) : x(x), y(y), c(c) {}
    bool operator < (const Node& node) const {
        return x < node.x || (x == node.x && y < node.y || (x == node.x && y == node.y && c < node.c));
    }
};
int n, m;
set<Node> st;
const int dx[] = {0, 1, 1, 1};
const int dy[] = {1, 0, 1, -1};
bool judge(int x, int y, int c) {
    for(int k = 0; k < 4; k++) {
        int cnt = 0;
        for(int i = -4; i <= 4; i++) {
            Node tmp(x+i*dx[k], y+i*dy[k], c);
            if(st.find(tmp) == st.end()) {
                cnt = 0; continue;
            }
            if(++cnt >= 5) return true;
        }
    }
    return false;
}
int main() {
    scanf("%d%d", &n, &m);
    for(int i = 0; i < m; i++) {
        int x, y; scanf("%d%d", &x, &y);
        st.insert(Node(x, y, i&1));
        printf(judge(x, y, i&1) ? "Y\n" : "N\n");
    }
    return 0;
}

领了TLE之后马上回去重算复杂度,在确认过不可能有O(n)的算法,以及我的算法已经到了O(nlogn)级别,就想优化常数
这份代码是把所有的点存放到一个set中,用一个参数c来标记颜色,所以我采取的第一个优化是开成两个set,用一个set来存黑棋,另一个来存白棋。这样就可以取消颜色的标记。而且算复杂度的时候,相当于将单次插入和查询的复杂度从 O ( l o g n ) O(logn) O(logn)优化到了 O ( l o g ( n 2 ) ) O(log(\frac{n}{2})) O(log(2n)),不过对于对数级别的复杂度来说,这个优化是非常小的了(比赛哪管那么多。。。想到什么写什么)
这就有了第二发TLE

#include<bits/stdc++.h>
using namespace std;
struct Node {
    int x, y;
    Node(int x, int y) : x(x), y(y) {}
    bool operator < (const Node& node) const {
        return x < node.x || (x == node.x && y < node.y);
    }
};
int n, m;
set<Node> st[2];
const int dx[] = {0, 1, 1, 1};
const int dy[] = {1, 0, 1, -1};
bool judge(int x, int y, int c) {
    for(int k = 0; k < 4; k++) {
        int cnt = 0;
        for(int i = -4; i <= 4; i++) {
            Node tmp(x+i*dx[k], y+i*dy[k]);
            if(st[c].find(tmp) == st[c].end()) {
                cnt = 0; continue;
            }
            if(++cnt >= 5) return true;
        }
    }
    return false;
}
int main() {
    scanf("%d%d", &n, &m);
    for(int i = 0; i < m; i++) {
        int x, y; scanf("%d%d", &x, &y);
        st[i&1].insert(Node(x, y));
        printf(judge(x, y, i&1) ? "Y\n" : "N\n");
    }
    return 0;
}

既然又是TLE,嗯其实我的思维还是慢了一拍。要做大量数据读入的代码(n=3e5)的常数级优化第一个想到的应该就是优读啊。
在这里解释一下优读是什么:比如我们读入一个字符,getchar()应该会比scanf("%c")快那么一点点,而scanf又会比cin快那么一点点。这一点点的速度一般来说是可以忽略不计的,但是在大量数据读入时,这个累积起来还是有一定效果的。
同理,读入一个整数时,scanf会比cin快,但是还有没有比scanf更快的呢?
嗯。。没有

但是你可以自己写啊!
所以就有了优读,通过较快的getchar()读入字符,然后进行模拟计算,从而达到读入整数的目的
这是我的优读板子:

int read() {
    int x = 0, k = 1; char ch = getchar();
    while('9' < ch || ch < '0') { if(ch == '-') k = -k; ch = getchar(); }
    while('0' <= ch && ch <= '9') { x = 10*x + ch - '0'; ch = getchar(); }
    return x*k;
}

他为什么会快?因为他的功能和灵活性和容错率比scanf都要弱,或者说他丢弃了很多功能(但是这些功能可能由于题目保证输入合法而不需要)。比如这个只能用来读整数(浮点数有另一个优读板子)。而且读整数不能读科学计数法形式的例如1e5.还有其他的。但是这些在做题的时候并不影响。
嗯所以用上了优读的我领到了第三发TLE

#include<bits/stdc++.h>
using namespace std;
struct Node {
    int x, y;
    Node(int x, int y) : x(x), y(y) {}
    bool operator < (const Node& node) const {
        return x < node.x || (x == node.x && y < node.y);
    }
};
int n, m;
set<Node> st[2];
const int dx[] = {0, 1, 1, 1};
const int dy[] = {1, 0, 1, -1};
bool judge(int x, int y, int c) {
    for(int k = 0; k < 4; k++) {
        int cnt = 0;
        for(int i = -4; i <= 4; i++) {
            Node tmp(x+i*dx[k], y+i*dy[k]);
            if(st[c].find(tmp) == st[c].end()) {
                cnt = 0; continue;
            }
            if(++cnt >= 5) return true;
        }
    }
    return false;
}
int read() {
    int x = 0, k = 1; char ch = getchar();
    while('9' < ch || ch < '0') { if(ch == '-') k = -k; ch = getchar(); }
    while('0' <= ch && ch <= '9') { x = 10*x + ch - '0'; ch = getchar(); }
    return x*k;
}
int main() {
    n = read(); m = read();
    for(int i = 0; i < m; i++) {
        int x, y; x = read(); y = read();
        st[i&1].insert(Node(x, y));
        puts(judge(x, y, i&1) ? "Y" : "N");
    }
    return 0;
}

既然优读也TLE了。。set也改过了。。那只能改我的judge了啊。。那只能在循环里面优化了。。
之前的循环因为想比较好写,所以写成了

    for(int k = 0; k < 4; k++) {
        int cnt = 0;
        for(int i = -4; i <= 4; i++) {
            Node tmp(x+i*dx[k], y+i*dy[k]);
            if(st[c].find(tmp) == st[c].end()) {
                cnt = 0; continue;
            }
            if(++cnt >= 5) return true;
        }
    }

这样其实只用判断4个方向,每个方向看有没有连续的5个同色的就可以了,但是代价是他可能会多进行几次不必要的运算。
既然要优化,那没办法,只能写长一点,变成了这样

    for(int k = 0; k < 4; k++) {
        int cnt = 1;
        for(int i = 1; i <= 4; i++) {
            Node tmp(x+i*dx[k], y+i*dy[k]);
            if(st[c].find(tmp) == st[c].end())
                break;
            cnt++;
        }
        for(int i = 1; i <= 4; i++) {
            Node tmp(x-i*dx[k], y-i*dy[k]);
            if(st[c].find(tmp) == st[c].end())
                break;
            cnt++;
        }
        if(cnt >= 5) return true;
    }

嗯然后就A了
现在我也不明白为什么这一点点优化可以把我从TLE拉到AC,甚至之后我有试过删掉优读,还是能AC。说明这里是优化幅度最大的部分。
嗯AC代码:

#include<bits/stdc++.h>
using namespace std;
struct Node {
    int x, y;
    Node(int x, int y) : x(x), y(y) {}
    bool operator < (const Node& node) const {
        return x < node.x || (x == node.x && y < node.y);
    }
};
int n, m;
set<Node> st[2];
const int dx[] = {0, 1, 1, 1};
const int dy[] = {1, 0, 1, -1};
bool judge(int x, int y, int c) {
    for(int k = 0; k < 4; k++) {
        int cnt = 1;
        for(int i = 1; i <= 4; i++) {
            Node tmp(x+i*dx[k], y+i*dy[k]);
            if(st[c].find(tmp) == st[c].end())
                break;
            cnt++;
        }
        for(int i = 1; i <= 4; i++) {
            Node tmp(x-i*dx[k], y-i*dy[k]);
            if(st[c].find(tmp) == st[c].end())
                break;
            cnt++;
        }
        if(cnt >= 5) return true;
    }
    return false;
}
int read() {
    int x = 0, k = 1; char ch = getchar();
    while('9' < ch || ch < '0') { if(ch == '-') k = -k; ch = getchar(); }
    while('0' <= ch && ch <= '9') { x = 10*x + ch - '0'; ch = getchar(); }
    return x*k;
}
int main() {
    n = read(); m = read();
    for(int i = 0; i < m; i++) {
        int x, y; x = read(); y = read();
        st[i&1].insert(Node(x, y));
        puts(judge(x, y, i&1) ? "Y" : "N");
    }
    return 0;
}

C 炫酷迷宫

这题没做,留题留坑,点击进入官方题解


链接:https://ac.nowcoder.com/acm/contest/331/C
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
Special Judge, 64bit IO Format: %lld

题目描述
小希现在需要你构建一个简单的方格图迷宫,’.'作为路,'x’作为障碍。
要求方格图大小为N*M,起点到终点的最短距离恰好为K。
方格图为四连通,即对于任何一个格子只能上下左右走,相邻格子距离为1,不能走出边界。

输入描述:
一行给出三个整数N,M,K,分别表示需要的方格图的行数,列数和起点到终点的最短距离。

1 ≤ N , M ≤ 1000 1≤N,M≤1000 1N,M1000
1 ≤ K ≤ N ∗ M 1≤K≤N∗M 1KNM
且保证可以构造出至少一张图使得最短距离为K。

输出描述:
第一行给出起点的坐标 x s , y s x_s,y_s xs,ys,坐标从1开始。
第二行给出终点的坐标 x t , y t x_t,y_t xt,yt
第三行开始输出N*M的方格图,N行,M列。
如果有多种方案,输出任意一种即可。

示例1

输入
3 3 5

输出
1 2
3 1
x…
xx.

示例2

输入
4 2 3

输出
4 1
2 2
xx
x.

.x

示例3

输入
1 10 9

输出
1 1
1 10


注:因markdown格式问题,复制过来的输入输出有变化,题目请参考原题链接


D 炫酷路途

链接:https://ac.nowcoder.com/acm/contest/331/D
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述
小希现在要从寝室赶到机房,路途可以按距离分为N段,第i个和i+1个是直接相连的,只需要一秒钟就可以相互到达。
炫酷的是,从第 i i i个到第 i + 2 p i+2^p i+2p个也是直接相连的(其中p为任意非负整数),只需要一秒钟就可以相互到达。
更炫酷的是,有K个传送法阵使得某些u,v之间也是直接相连的,只需要一秒钟就可以相互到达,当然,由于设备故障,可能会有一些u=v的情况发生。
现在小希为了赶路,她需要在最短的时间里从寝室(编号为1)到达机房(编号为N),她不希望到达这N个部分以外的地方(不能到达位置N+1),也不能走到比自己当前位置编号小的地方(比如从5走到3是非法的)。
她想知道最短的时间是多少。

输入描述:
第一行输入两个整数N,K,表示路途的段数和传送法阵的数量。
从第二行开始K行,每行两个整数 a i , b i a_i,b_i ai,bi表示两个位置之间相连。

2 ≤ N ≤ 1 , 000 , 000 , 000 2≤N≤1,000,000,000 2N1,000,000,000
0 ≤ K ≤ 15 0≤K≤15 0K15

输出描述:
输出一个整数,表示从寝室到机房最短的时间是多少秒。

示例1

输入
12 2
1 5
6 6

输出
3

示例2

输入
17 2
2 5
8 9

输出
1


嗯一看到这题
????最短路????BFS啊????
看看,就算不用传送,可以走任意的2^n,那这个数据范围最多也就走30步一定到了啊
嗯深度不高,可以试试
然后刚摸到键盘,等等、。、、每个结点可以有30种走法,深度是30, 3 0 30 30^{30} 3030,那不完了吗
然后发现数据范围。 k ≤ 15 k \leq 15 k15????
嗯可以枚举每个传送门是否使用,然后已知起点和终点的不用传送门的路径所需步数是可以直接算出来的。那不就是个。。数位枚举嘛
嗯关于不用传送门的路怎么算,假设路径长度为d,把d写成2进制,2进制中的每一个1都需要走1步,也就是说二进制中有多少个1就要走几步。1e9的数据范围的二进制最多30位,所以之前说不用传送最多也只需要30步
算复杂度,15个传送门的使用情况有 2 15 2^{15} 215种,也才3e4+。单次操作可以认为是常数级别的,常数也不会大到1e4这个级别,所以过是稳稳的

#include<bits/stdc++.h>
using namespace std;
struct Node {
    int u, v;
    bool operator < (const Node& node) const {
        return u <= node.u;
    }
} node[20];
int step(int d) {
    int ans = 0;
    while(d) {
        d -= d & -d;
        ans++;
    }
    return ans;
}
int main() {
    int n, k; scanf("%d%d", &n, &k);
    for(int i = 0; i < k; i++) scanf("%d%d", &node[i].u, &node[i].v);
    sort(node, node+k);
    int ans = step(n-1);
    for(int i = 0; i < 1<<k; i++) {
        int x = 1, tmp = 0;
        for(int j = 0; j < k; j++) {
            if(i & 1<<j) {
                if(x > node[j].u) continue;
                tmp += step(node[j].u-x) + 1;
                x = node[j].v;
            }
        }
        if(x > n) continue;
        tmp += step(n-x);
        ans = min(ans, tmp);
    }
    printf("%d\n", ans);
    return 0;
}

E 炫酷划线

这题没做,留题留坑,点击进入官方题解


链接:https://ac.nowcoder.com/acm/contest/331/E
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述
平面上有一个圆,圆环上按顺时针顺序分布着从1到n,一共n个点。
现在无聊的小希开始按某种顺序对其在圆内两两连线,小希尽量避免让两条线碰撞,可是有的时候,这显然避免不了。
现在你知道小希划线的顺序是什么,请你判断小希在最优情况下,什么时候会被迫使得线相交,输出最早的时刻(是第几条线)。

输入描述:
数据第一行一个整数T,表示数据组数。
每组数据第一行输入两个整数N,M,代表点的个数和游戏进行的轮数。
随后M行,每行两个整数 a i , b i a_i,b_i ai,bi,表示两个点之间连线。
数据保证每个点最多被连线一次。

T ≤ 10 T≤10 T10
1 ≤ N , M ≤ 100000 1≤N,M≤100000 1N,M100000
1 ≤ a i , b i ≤ 100000 1≤a_i,b_i≤100000 1ai,bi100000

输出描述:
对于每组数据,一行。
如果中途某一条线开始无法避免相交,则输出当轮轮数。
否则,输出-1。

示例1

输入
2
10 4
5 3
1 9
2 6
7 10
4 2
1 2
3 4

输出
4
-1


虽然这题没写代码,但是我确实是在经历过很多思考之后,估复杂度或者想策略都过不去,才最终放弃的。结果最后群里有人暴力写过了????1e5的范围用暴力????就算不是纯暴力。。。带个二分也是 n 2 l o g n n^2logn n2logn的复杂度吧,。。这怎么可能过。。。真的不敢写。。。反正出题人背锅


F 炫酷回文

这题没做,留题留坑,点击进入官方题解


链接:https://ac.nowcoder.com/acm/contest/331/F
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述
小希拿到了一个很长的两行的字符矩阵,她想知道里面有多少个子矩阵的字符取出后通过某种排列可以构成一个回文串。
该字符矩阵内仅含有数字0-9。

输入描述:
第一行输入一个整数N,表示字符矩阵的长度为N。
随后两行,每行N个字符,表示字符矩阵。
1 ≤ N ≤ 1000000 1≤N≤1000000 1N1000000

输出描述:
输出一个整数,表示有多少个子矩阵的字符取出后通过某种排列可以构成一个回文串。

示例1

输入
2
00
00

输出
9

示例2

输入
10
0313512342
1231345123

输出
28


其实我猜到了是维护前缀异或和。。。但是因为细节比较多最后还是没写。。。基础不扎实啊


G 炫酷数字

链接:https://ac.nowcoder.com/acm/contest/331/G
来源:牛客网

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述
小希希望你构造一个最小的正整数,使得其有n个因子。
输入描述:
第一行一个整数T表示数据组数
每组数据第一行输入一个正整数n,表示其因子数。

n ≤ 1 , 000 , 000 n≤1,000,000 n1,000,000
T ≤ 1 , 000 , 000 T≤1,000,000 T1,000,000

输出描述:
输出一行一个整数,表示你构造出的这个数。注意:你需要保证你构造的数 ≤ 1 , 000 , 000 ≤1,000,000 1,000,000,如果在这个范围里面无法构造出一个正整数满足条件,请输出-1。

示例1

输入
2
4
5

输出
6
16


第一反应居然是欧拉函数。。。脑子有点抽
第二反应是拓展筛法。。
筛法思维的来源是,求一个数是不是质数,可以直接用 O ( n ) O(\sqrt n) O(n )暴力判断。。也可以用筛法预处理,就是先标记所有的数都是素数,然后从小到大遍历这个表,每次遍历到一个数,如果它是质数,就把他的倍数标记为不是质数。这样的理论复杂度应该在O(nlogn)这个量级上面(不严谨)。其实质数的筛法还能优化到线性筛,即复杂度是线性的O(n),这里不详细讲了
这题也可以类似的使用筛法。初始化每个数的因数个数为0,然后每次遍历到一个数,就让其倍数的因数个数+1,这样就能预处理出所有的数的因数个数。再把最小的那一个存下来就可以了。

群里的另一种方法是,本地预处理出n=1到240(左右)的所有的答案,剩下的直接输出-1就好了。这种方法的原因,显然随着因子个数的增加,这个数大小的增加是非常快的。240其实是已经打表打出来之后的答案,但是可以预计到,打个几百个,一定结果会超出题目要求的数据范围。

还是贴我自己的代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
int cnt[maxn], num[maxn];
int main() {
	memset(num, -1, sizeof(num));
	for(int i = 1; i < maxn; i++)
		for(int j = i; j < maxn; j += i)
			cnt[j]++;
	for(int i = 1; i < maxn; i++)
		if(num[cnt[i]] == -1) num[cnt[i]] = i;
	
	int T; scanf("%d", &T);
	while(T--) {
		int n; scanf("%d", &n);
		printf("%d\n", num[n]);
	}
    return 0;
}

H 炫酷雪花

链接:https://ac.nowcoder.com/acm/contest/331/H
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld

题目描述
小希在家里做着作业,外面飘起了斗大的雪花,很冷!
小希把接下来连续的要做作业的时间分成n个单位,每个单位时间内小希都会受到 a i a_i ai的寒冷值侵袭,她可以选择在任何一些时间站起来蹦蹦跳跳,以使得这个单位的寒冷值不侵袭她。
小希最大能承受的寒冷程度是K,但是她想选择尽可能多的时间做作业,请你帮帮她!
小希受到的寒冷程度即为不蹦蹦跳跳的时间的寒冷值总和。

输入描述:
第一行两个整数n,k表示时间总长为n个单位,小希最大能承受的寒冷程度是K。
随后一行n个整数,第i个整数表示第i个时间单位小希会受到 a i a_i ai的寒冷值。

1 ≤ n ≤ 5 , 000 1≤n≤5,000 1n5,000
0 ≤ K ≤ 1 , 000 , 000 , 000 , 000 , 000 0≤K≤1,000,000,000,000,000 0K1,000,000,000,000,000
0 ≤ a i ≤ 1 , 000 , 000 , 000 0≤a_i≤1,000,000,000 0ai1,000,000,000

输出描述:
第一行输出一个整数ans表示小希最多的学习时间。
第二行输出一个字符串,表示一个可行的方案,长度为n,第i个字符为1表示这个单位时间站起来蹦蹦跳跳,为0表示这个单位时间好好学习。
如果有多种可行方案,请输出字典序最小的。

示例1

输入
3 5
1 2 3

输出
2
001

示例2

输入
5 9
3 3 3 3 3

输出
3
00011


emmmmm这题如果说输出任意一种,那就是签到题。。。可以直接不断选最小的ai,知道不能加为止
难点在字典序最小上面。。我倒是有想法。。。不过还没写出来、。、、还是等写完再补吧。、。。。


I 炫酷镜子

链接:https://ac.nowcoder.com/acm/contest/331/I
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述
小希拿到了一个镜子块,镜子块可以视为一个N x M的方格图,里面每个格子仅可能安装\或者/的镜子,会反射90°光线,也可能没有安装镜子,使用.代替。
但她看不清楚里面的镜子构造是怎样的。
你是这块镜子块的主人,所以你想计算这块镜子块(从输入的上方往下射入光线)从左到右每一格射入依次分别会从最下面的哪一格子射出,如果无法射出,输出-1。

输入描述:
第一行输入两个整数N,M。随后的N行,每行M个字符。
1 ≤ N , M ≤ 500 1≤N,M≤500 1N,M500

输出描述:
输出M行整数,依次为从第i个格子从上往下射入时从下面的哪里射出,如果光线不会从下面射出,则输出-1。

示例1

输入
3 3


.\

输出
3
2
-1

说明
第一列遇到镜子两次反弹通过第三列射出。
第二列直接射出。
第三列因为镜子反弹后向右边射出。


注:因markdown格式问题,复制过来的输入输出有变化,题目请参考原题链接


emmmm这题没啥好解释的吧。。。。纯暴力模拟就完了。。。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 505;
int n, m;
char s[maxn][maxn];
int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        getchar();
        for(int j = 1; j <= m; j++)
            s[i][j] = getchar();
    }
     
    for(int i = 1; i <= m; i++) {
        int x = 1, y = i, dx = 1, dy = 0;
        while((1 <= x && x <= n) && (1 <= y && y <= m)) {
            if(s[x][y] == '/') {
                int t = dy;
                dy = -dx;
                dx = -t;
            }
            else if(s[x][y] == '\\')
                swap(dx, dy);
            x += dx; y += dy;
        }
        if(x != n+1) y = -1;
        printf("%d\n", y);
    }
    return 0;
}

J 炫酷数学

链接:https://ac.nowcoder.com/acm/contest/331/J
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述
小希最近想知道一个东西,就是A+B=A|B(其中|为按位或)的二元组有多少个。
当然,直接做这个式子对小希来说太难了,所以小希改变了一些条件,她仅想知道其中A,B<N的情况,其中N为2的幂次。
当然,(A=1,B=0)和(A=0,B=1)被认为是不同的二元组。

输入描述:
第一行输入一个非负整数M。
N = 2 M , M ≤ 100 N=2^M,M≤100 N=2M,M100
即2的M次为N。

输出描述:
一个整数ans,对998244353取模。

示例1

输入
0

输出
1

示例2

输入
71

输出
588378066


嗯打个表很容易发现结果是3的n次幂
那么简单证明一下
很显然a+b=a|b等式成立的条件是a和b的二进制表示中,在同一位不能全为1
假设现在a+b=a|b,a和b是已知的,且a,b写成二进制均为n-1位数(可含前导零)。那么现在把a,b添加一位数,变成n位数(显然n位数都能从n-1位变过来),如果等式仍要成立的话,那么只有3种情况

  1. a后添0,b后添0
  2. a后添0,b后添1
  3. a后添1,b后添0
    也就是说从n-1位数变为n位数,组合的方法数变为原来的3倍,且n=0时答案为1显然。
#include<bits/stdc++.h>
using namespace std;
const int mod = 998244353;
long long qpow(long long a, int n) {
    long long ans = 1;
    while(n) {
        if(n&1) ans = ans*a%mod;
        a = a*a%mod;
        n >>= 1;
    }
    return ans;
}
int main() {
    int n; cin>>n;
    printf("%lld\n", qpow(3, n));
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值