CSPJ2020A. 优秀的拆分 (Excellent Split)
题目描述
一般来说,一个正整数可以拆分成若干个正整数的和。
例如,1=11=11=1,10=1+2+3+410=1+2+3+410=1+2+3+4 等。对于正整数 nnn 的一种特定拆分,我们称它为“优秀的”,当且仅当在这种拆分下,nnn 被分解为了若干个不同的 222 的正整数次幂。注意,一个数 xxx 能被表示成 222 的正整数次幂,当且仅当 xxx 能通过正整数个 222 相乘在一起得到。
例如,10=8+2=23+2110=8+2=2^3+2^110=8+2=23+21 是一个优秀的拆分。但是,7=4+2+1=22+21+207=4+2+1=2^2+2^1+2^07=4+2+1=22+21+20 就不是一个优秀的拆分,因为 111 不是 222 的正整数次幂。
现在,给定正整数 nnn,你需要判断这个数的所有拆分中,是否存在优秀的拆分。若存在,请你给出具体的拆分方案。
输入格式
输入只有一行,一个整数 nnn,代表需要判断的数。
输出格式
如果这个数的所有拆分中,存在优秀的拆分。那么,你需要从大到小输出这个拆分中的每一个数,相邻两个数之间用一个空格隔开。可以证明,在规定了拆分数字的顺序后,该拆分方案是唯一的。
若不存在优秀的拆分,输出 -1
。
数据范围与提示
对于 20%20\%20% 的数据,n≤10n \le 10n≤10。
对于另外 20%20\%20% 的数据,保证 nnn 为奇数。
对于另外 20%20\%20% 的数据,保证 nnn 为 222 的正整数次幂。
对于 80%80\%80% 的数据,n≤1024n \le 1024n≤1024。
对于 100%100\%100% 的数据,1≤n≤1071 \le n \le {10}^71≤n≤107。
#include<bits/stdc++.h>
using namespace std;
long long q,w,e,r,t,y,u,i,o,p,s,d,f,g,h,j,k,l,m,n,v,x,z,kk;
int b[1000];
int a[1000];
int c[1000];
int main()
{
cin>>n;
if(n%2==1)
{
cout<<"-1";
return 0;
}
i=1;x=2;
while(n!=0)
{
if(x*2<=n)
x*=2;
if(x*2>n)
{
n-=x;
cout<<x<<" ";
x=2;
}
i++;
}
return 0;
}
CSPJ2020B. 直播获奖 (Live Awards Ceremony)
题目描述
NOI2130 即将举行。为了增加观赏性,CCF 决定逐一评出每个选手的成绩,并直播即时的获奖分数线。本次竞赛的获奖率为 w%w\%w%,即当前排名前 w%w\%w% 的选手的最低成绩就是即时的分数线。
更具体地,若当前已评出了 ppp 个选手的成绩,则当前计划获奖人数为 max(1,⌊p∗w%⌋)\max(1, \lfloor p * w \%\rfloor)max(1,⌊p∗w%⌋),其中 www 是获奖百分比,⌊x⌋\lfloor x \rfloor⌊x⌋ 表示对 xxx 向下取整,max(x,y)\max(x,y)max(x,y) 表示 xxx 和 yyy 中较大的数。如有选手成绩相同,则所有成绩并列的选手都能获奖,因此实际获奖人数可能比计划中多。
作为评测组的技术人员,请你帮 CCF 写一个直播程序。
输入格式
第一行有两个整数 n,wn, wn,w。分别代表选手总数与获奖率。 第二行有 nnn 个整数,依次代表逐一评出的选手成绩。
输出格式
只有一行,包含 nnn 个非负整数,依次代表选手成绩逐一评出后,即时的获奖分数线。相邻两个整数间用一个空格分隔。
样例 1 解释
数据范围与提示
各测试点的 nnn 如下表:
测试点编号 | n= |
---|---|
1∼31 \sim 31∼3 | 101010 |
4∼64 \sim 64∼6 | 500500500 |
7∼107 \sim 107∼10 | 200020002000 |
11∼1711 \sim 1711∼17 | 10410^4104 |
18∼2018 \sim 2018∼20 | 10510^5105 |
对于所有测试点,每个选手的成绩均为不超过 600600600 的非负整数,获奖百分比 www 是一个正整数且 1≤w≤991 \le w \le 991≤w≤99。
在计算计划获奖人数时,如用浮点类型的变量(如 C/C++ 中的 float 、 double,Pascal 中的 real 、 double 、 extended 等)存储获奖比例 w%w\%w%,则计算 5×60%5 \times 60\%5×60% 时的结果可能为 3.0000013.0000013.000001,也可能为 2.9999992.9999992.999999,向下取整后的结果不确定。因此,建议仅使用整型变量,以计算出准确值。
#include<bits/stdc++.h>
using namespace std;
const int mx=610;
int c[mx];//x分的人有多少个
int n,w,ans,cnt,x,i;
int main()
{
for(int i=0; i<=600; i++) c[i]=0;
cin>>n>>w;
for (int i=1; i<=n; i++)
{
cin>>x;
c[x]++;
cnt=floor(i*w/100);
cnt=max(cnt,1);
ans=600;
int s=c[ans];
while (s<cnt)
{
ans--;
s+=c[ans];
}
cout<<ans<<' ';
}
return 0;
}
CSPJ2020C. 表达式 (Expression)
题目描述
小 C 热衷于学习数理逻辑。有一天,他发现了一种特别的逻辑表达式。在这种逻辑表达式中,所有操作数都是变量,且它们的取值只能为 000 或 111,运算从左往右进行。如果表达式中有括号,则先计算括号内的子表达式的值。特别的,这种表达式有且仅有以下几种运算:
- 与运算:
a & b
。当且仅当 aaa 和 bbb 的值都为 111 时,该表达式的值为 111。其余情况该表达式的值为 000。 - 或运算:
a | b
。当且仅当 aaa 和 bbb 的值都为 000 时,该表达式的值为 000。其余情况该表达式的值为 111。 - 取反运算:
!a
。当且仅当 aaa 的值为 000 时,该表达式的值为 111。其余情况该表达式的值为 000。
小 C 想知道,给定一个逻辑表达式和其中每一个操作数的初始取值后,再取反某一个操作数的值时,原表达式的值为多少。
为了化简对表达式的处理,我们有如下约定:
表达式将采用后缀表达式的方式输入。
后缀表达式的定义如下:
- 如果 EEE 是一个操作数,则 EEE 的后缀表达式是它本身。
- 如果 EEE 是 E1 op E2E_1~\texttt{op}~E_2E1 op E2 形式的表达式,其中 op\texttt{op}op 是任何二元操作符,且优先级不高于 E1E_1E1、E2E_2E2 中括号外的操作符,则 EEE 的后缀式为 E1′E2′opE_1' E_2' \texttt{op}E1′E2′op,其中 E1′E_1'E1′、E2′E_2'E2′ 分别为 E1E_1E1、E2E_2E2 的后缀式。
- 如果 EEE 是 E1E_1E1 形式的表达式,则 E1E_1E1 的后缀式就是 EEE 的后缀式。
同时为了方便,输入中:
与运算符(&
)、或运算符(|
)、取反运算符(!
)的左右均有一个空格,但表达式末尾没有空格。
操作数由小写字母 x
与一个正整数拼接而成,正整数表示这个变量的下标。例如:x10
,表示下标为 10
的变量 x10x_{10}x10 。数据保证每个变量在表达式中出现恰好一次。
输入格式
第一行包含一个字符串 sss,表示上文描述的表达式。
第二行包含一个正整数 nnn,表示表达式中变量的数量。表达式中变量的下标为 1,2,⋯ ,n1,2, \cdots , n1,2,⋯,n。
第三行包含 nnn 个整数,第 iii 个整数表示变量 xix_ixi 的初值。
第四行包含一个正整数 qqq,表示询问的个数。
接下来 qqq 行,每行一个正整数,表示需要取反的变量的下标。注意,每一个询问的修改都是临时的,即之前询问中的修改不会对后续的询问造成影响。
数据保证输入的表达式合法。变量的初值为 000 或 111。
输出格式
输出一共有 qqq 行,每行一个 000 或 111,表示该询问下表达式的值。
样例 1 解释
该后缀表达式的中缀表达式形式为 (x1&x2)∣x3(x_1 \& x_2) | x_3(x1&x2)∣x3。
对于第一次询问,将 x1x_1x1 的值取反。此时,三个操作数对应的赋值依次为 000,000,111。原表达式的值为 (0&0)∣1=1(0\&0)|1=1(0&0)∣1=1。
对于第二次询问,将 x2x_2x2 的值取反。此时,三个操作数对应的赋值依次为 111,111,111。原表达式的值为 (1&1)∣1=1(1\&1)|1=1(1&1)∣1=1。
对于第三次询问,将 x3x_3x3 的值取反。此时,三个操作数对应的赋值依次为 111,000,000。原表达式的值为 (1&0)∣0=0(1\&0)|0=0(1&0)∣0=0。
样例 2 解释
该表达式的中缀表达式形式为 (!x1)&(!((x2∣x4)&(x3&(!x5))))(!x_1)\&(!((x_2|x_4)\&(x_3\&(!x_5))))(!x1)&(!((x2∣x4)&(x3&(!x5))))。
数据范围与提示
对于 20%20\%20% 的数据,表达式中有且仅有与运算(&
)或者或运算(|
)。
对于另外 30%30\%30% 的数据,∣s∣≤1000|s| \le 1000∣s∣≤1000,q≤1000q \le 1000q≤1000,n≤1000n \le 1000n≤1000。
对于另外 20%20\%20% 的数据,变量的初值全为 000 或全为 111。
对于 100%100\%100% 的数据,1≤∣s∣≤1×1061 \le |s| \le 1 \times 10^61≤∣s∣≤1×106,1≤q≤1×1051 \le q \le 1 \times 10^51≤q≤1×105,2≤n≤1×1052 \le n \le 1 \times 10^52≤n≤1×105。
其中,∣s∣|s|∣s∣ 表示字符串 sss 的长度。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const int N = 1000005;
char s[N];
int a[N];
int son[N][2], ck;
int flag[N], c[N];
int n, q;
int dfs(int u, int g) {
a[u] ^= g;
if (u <= n) {
return a[u];
}
int x = dfs(son[u][0], g ^ flag[son[u][0]]);
int y = dfs(son[u][1], g ^ flag[son[u][1]]);
if (a[u] == 2) {
if (x == 0) c[son[u][1]] = 1;
if (y == 0) c[son[u][0]] = 1;
return x & y;
} else {
if (x == 1) c[son[u][1]] = 1;
if (y == 1) c[son[u][0]] = 1;
return x | y;
}
}
void dfs2(int u) {
if (u <= n) return;
c[son[u][0]] |= c[u];
c[son[u][1]] |= c[u];
dfs2(son[u][0]);
dfs2(son[u][1]);
}
int main() {
// freopen("expr.in", "r", stdin);
// freopen("expr.out", "w", stdout);
gets(s);
scanf("%d", &n);
ck = n;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
stack<int> b;
for (int i = 0; s[i]; i += 2) {
if (s[i] == 'x') {
int x = 0;
i++;
while (s[i] != ' ') {
x = x * 10 + s[i] - '0';
i++;
}
i--;
b.push(x);
} else if (s[i] == '&') {
int x = b.top();
b.pop();
int y = b.top();
b.pop();
b.push(++ck);
a[ck] = 2;
son[ck][0] = x;
son[ck][1] = y;
} else if (s[i] == '|') {
int x = b.top();
b.pop();
int y = b.top();
b.pop();
b.push(++ck);
a[ck] = 3;
son[ck][0] = x;
son[ck][1] = y;
} else if(s[i] == '!'){
flag[b.top()] ^= 1;
}
}
int ans = dfs(ck, flag[ck]);
dfs2(ck);
scanf("%d", &q);
while (q--) {
int x;
scanf("%d", &x);
printf("%d\n", c[x] ? ans : !ans);
}
return 0;
}
CSPJ2020D. 方格取数 (Grid Numbers Picking)
题目描述
设有 n×mn \times mn×m 的方格图,每个方格中都有一个整数。现有一只小熊,想从图的左上角走到右下角,每一步只能向上、向下或向右走一格,并且不能重复经过已经走过的方格,也不能走出边界。小熊会取走所有经过的方格中的整数,求它能取到的整数之和的最大值。
输入格式
第一行有两个整数 n,mn, mn,m。
接下来 nnn 行每行 mmm 个整数,依次代表每个方格中的整数。
输出格式
一个整数,表示小熊能取到的整数之和的最大值。
样例 1 解释
样例 2 解释
数据范围与提示
对于 20%20\%20% 的数据,n,m≤5n, m \le 5n,m≤5。
对于 40%40\%40% 的数据,n,m≤50n, m \le 50n,m≤50。
对于 70%70\%70% 的数据,n,m≤300n, m \le 300n,m≤300。
对于 100%100\%100% 的数据,1≤n,m≤1031 \le n,m \le 10^31≤n,m≤103。
方格中整数的绝对值不超过 10410^4104。
#include <stdio.h>
typedef long long LL;
const LL min_ll = -1e18;
int n, m; LL w[1005][1005], f[1005][1005][2];
inline LL mx(LL p, LL q, LL r) {return p > q ? (p > r ? p : r) : (q > r ? q : r);}
inline LL dfs(int x, int y, int from) {
if (x < 1 || x > n || y < 1 || y > m) return min_ll;
if (f[x][y][from] != min_ll) return f[x][y][from];
if (from == 0) f[x][y][from] = mx(dfs(x + 1, y, 0), dfs(x, y - 1, 0), dfs(x, y - 1, 1)) + w[x][y];
else f[x][y][from] = mx(dfs(x - 1, y, 1), dfs(x, y - 1, 0), dfs(x, y - 1, 1)) + w[x][y];
return f[x][y][from];
}
int main(void) {
// freopen("number.in", "r", stdin); freopen("number.out", "w", stdout);
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
scanf("%lld", &w[i][j]);
f[i][j][0] = f[i][j][1] = min_ll;
}
f[1][1][0] = f[1][1][1] = w[1][1];
printf("%lld\n", dfs(n, m, 1));
return 0;
}