[CF666B]World Tour 题解
题目地址:Codef …
May all the beauty be blessed.
题目地址:51Nod:子集价值 问题 – 51Nod
lyk最近在研究位运算。它发现除了xor,or,and外还有很多运算。它新定义了一种运算符“#”。具体地,可以由4个参数来表示。ai,j表示i#j。其中i,j与a的值均∈[0,1]。当然问题可以扩展为>1的情况,具体地,可以将两个数分解为p位,然后对于每一位执行上述的位运算,再将这个二进制串转化为十进制就可以了。例如当 a0,0=a1,1=0,a0,1=a1,0=1时,3#4在p=3时等于7,2#3在p=4时等于1(实际上就是异或运算)。
现在lyk想知道的是,已知一个数列b。它任意选取一个序列c,满足 c1<c2<…<ck,其中1≤c1且ck≤n ,这个序列的价值为 bc1 # bc2 #…# bck 的平方。这里我们假设k是正整数,因此满足条件的c的序列一定是 2n−1 。lyk想知道所有满足条件的序列的价值总和是多少。例如样例中,7个子集的价值分别为1,1,4,4,9,9,0。总和为28。
由于答案可能很大,只需对1,000,000,007取模即可。
输入格式:
第一行两个整数n(1<=n<=50000),p(1<=p<=30)。
第二行4个数表示a0,0,a0,1,a1,0,a1,1。(这4个数都∈{0,1})
第三行n个数bi(0<=bi<2^p)。
输出格式:
一行表示答案。
输入样例#1:
3 30 0 1 1 0 1 2 3
输出样例#1:
28
又是一道喜闻乐见的数位DP。
首先这个平方给我们设置了一个障碍,我们如果不求平方只求和,那么我们可以设计状态dp[i][0/1]表示当前处理到第i个数,现在正在处理的这一位经过运算后为0或1的方案总数。我们从大到小枚举每一位,每一位对答案的贡献就是1<<数*dp[n][1]。
但是现在有一个平方,那我们考虑平方的意义。如果把每一位单独拆出来算,那么一个十进制数可以表示成一堆2的幂加起来的结果,又由于我们有下面的式子
(A_1+A_2+A_3+A_4+\cdots+A_n)^2 = A_1A_2 + \cdots + A_1A_n + A_2A_1 + \cdots + A_2A_n + \cdots + A_n^2
我们发现,平方后的结果的每一项只跟原来的两项有关系。换句话说,原来的数中两个位就能对平方后的答案产生一个位的贡献。那我们考虑把上面求和的DP状态扩展为dp[i][0/1][0/1]表示当前处理到第i个数,选定的两位分别为多少的方案总数。我们枚举选定哪两位来组合出答案,每枚举一次就DP一次,由于两位必须得都是1乘起来才非0,最后枚举的两位产生的贡献就是dp[n][1][1]*1<<(i+j),其中i和j是枚举的两位。
这样做的时间复杂度是O(p^2n)的。
// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 50005, MO = 1e9 + 7;
int n, p, op[2][2];
LL dp[MAXN][2][2], a[MAXN];
int main() {
n = readint();
p = readint();
op[0][0] = readint();
op[0][1] = readint();
op[1][0] = readint();
op[1][1] = readint();
for(int i = 1; i <= n; i++) {
a[i] = readint();
}
LL ans = 0;
for(int i = 0; i < p; i++) {
for(int j = 0; j < p; j++) {
memset(dp, 0, sizeof(dp));
for(int k = 1; k <= n; k++) {
int p1 = (a[k] >> i) & 1, p2 = (a[k] >> j) & 1;
dp[k][p1][p2] = (dp[k][p1][p2] + 1) % MO;
dp[k][op[0][p1]][op[0][p2]] = (dp[k][op[0][p1]][op[0][p2]] + dp[k - 1][0][0]) % MO;
dp[k][0][0] = (dp[k][0][0] + dp[k - 1][0][0]) % MO;
dp[k][op[0][p1]][op[1][p2]] = (dp[k][op[0][p1]][op[1][p2]] + dp[k - 1][0][1]) % MO;
dp[k][0][1] = (dp[k][0][1] + dp[k - 1][0][1]) % MO;
dp[k][op[1][p1]][op[0][p2]] = (dp[k][op[1][p1]][op[0][p2]] + dp[k - 1][1][0]) % MO;
dp[k][1][0] = (dp[k][1][0] + dp[k - 1][1][0]) % MO;
dp[k][op[1][p1]][op[1][p2]] = (dp[k][op[1][p1]][op[1][p2]] + dp[k - 1][1][1]) % MO;
dp[k][1][1] = (dp[k][1][1] + dp[k - 1][1][1]) % MO;
}
ans = (ans + (1ll << (i + j)) % MO * dp[n][1][1] % MO) % MO;
}
}
printf("%lld", ans);
return 0;
}
题目地址:HDUOJ:Problem – 3022、Timus Online Judge:1658. Sum of Digits @ Timus Online Judge
Petka thought of a positive integer n and reported to Chapaev the sum of its digits and the sum of its squared digits. Chapaev scratched his head and said: “Well, Petka, I won’t find just your number, but I can find the smallest fitting number.” Can you do the same?
给定一个数字每位上数字的和和平方和,求符合条件的最小数字。
输入格式:
The first line contains the number of test cases t (no more than 10000). In each of the following t lines there are numbers s1 and s2 (1 ≤ s1, s2 ≤ 10000) separated by a space. They are the sum of digits and the sum of squared digits of the number n.
输出格式:
For each test case, output in a separate line the smallest fitting number n, or “No solution” if there is no such number or if it contains more than 100 digits.
输入样例#1:
4 9 81 12 9 6 10 7 9
输出样例#1:
9 No solution 1122 111112
是一个很巧妙的DP。
首先s1≤900,s2≤8100是很好确定的。大于的情况直接无解。
首先,考虑两个DP,f[i][j]代表和为i平方和为j的数字最小长度,g[i][j]代表和为i平方和为j的最小开头数字。容易知道最优解数字中肯定不含0。
我们枚举i和j,然后从小到大枚举扩展长度的数字k,如果在f[i][j]的基础上加入k后长度比f[i+k][j+k*k]更小,那么应当更新答案,此时k也就是g[i][j]的值。如果加入k后与f[i+k][j+k*k]长度一样,那么g[i][j]看一下是否比k大,更新g[i][j]答案。这是一个很暴力的转移。
上面的两个DP数组可以预处理出来,查询的时候先看是否可行,是否超100,然后每次用g[i][j]输出,s1-=g[i][j],s2-=g[i][j]*g[i][j],直到s1或s2为0。
// Code by KSkun, 2018/3
#include <cstdio>
#include <algorithm>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline int readint() {
register int res = 0, neg = 1;
char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN1 = 910, MAXN2 = 8110;
int T, s1, s2, f[MAXN1][MAXN2], g[MAXN1][MAXN2];
inline int sqr(int x) {
return x * x;
}
inline void init() {
for(int i = 1; i <= 9; i++) {
f[i][sqr(i)] = 1;
g[i][sqr(i)] = i;
}
for(int i = 1; i <= 900; i++) {
for(int j = 1; j <= 8100; j++) {
if(f[i][j]) {
for(int k = 1; k <= 9; k++) {
if(!f[i + k][j + sqr(k)] || f[i + k][j + sqr(k)] > f[i][j] + 1) {
f[i + k][j + sqr(k)] = f[i][j] + 1;
g[i + k][j + sqr(k)] = k;
} else if(f[i + k][j + sqr(k)] == f[i][j] + 1) {
g[i + k][j + sqr(k)] = std::min(g[i + k][j + sqr(k)] = k, k);
}
}
}
}
}
}
int main() {
T = readint();
init();
while(T--) {
s1 = readint();
s2 = readint();
if(s1 > 900 || s2 > 8100 || !f[s1][s2] || f[s1][s2] > 100) {
printf("No solution\n");
continue;
}
while(s1 && s2) {
int t = g[s1][s2];
printf("%d", t);
s1 -= t;
s2 -= sqr(t);
}
printf("\n");
}
return 0;
}
题目地址:POJ:3734 — Blocks
Panda has received an assignment of painting a line of blocks. Since Panda is such an intelligent boy, he starts to think of a math problem of painting. Suppose there are N blocks in a line and each block can be paint red, blue, green or yellow. For some myterious reasons, Panda want both the number of red blocks and green blocks to be even numbers. Under such conditions, Panda wants to know the number of different ways to paint these blocks.
给一排砖块涂色,一共有红蓝绿黄4种颜色,要求最后红色和绿色砖块数一定是偶数,求合法的涂色方案数。
输入格式:
The first line of the input contains an integer T(1≤T≤100), the number of test cases. Each of the next T lines contains an integer N(1≤N≤10^9) indicating the number of blocks.
输出格式:
For each test cases, output the number of ways to paint the blocks in a single line. Since the answer may be quite large, you have to module it by 10007.
输入样例#1:
2 1 2
输出样例#1:
2 6
我们用dp[i][0/1/2]表示涂到第i个砖块,当前红绿砖块分别全是奇数、一奇一偶、全是偶数时合法的方案数。我们可以通过下面的式子来转移。
\begin{matrix} dp[i][0] = 2dp[i - 1][0] + dp[i - 1][1] \\ dp[i][1] = 2dp[i - 1][0] + 2dp[i - 1][1] + 2dp[i - 1][2] \\ dp[i][2] = dp[i - 1][1] + 2dp[i - 1][2] \end{matrix}
我一看,人群裆中……不对,这个好像可以用矩阵来表示转移。表示出来是下面这个样子
\begin{pmatrix} dp[i][0] \\ dp[i][1] \\ dp[i][2] \end{pmatrix} = \begin{pmatrix} dp[i-1][0] \\ dp[i-1][1] \\ dp[i-1][2] \end{pmatrix} \times \begin{pmatrix} 2 & 1 & 0 \\ 2 & 2 & 2 \\ 0 & 1 & 2 \end{pmatrix}
那显然这个可以矩阵快速幂算一下,把后面那个转移矩阵n次方一下就好了。
// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline int readint() {
register int res = 0, neg = 1;
char c = fgc();
while(c < '0' || c > '9') {
if(c == '-') neg = -1;
c = fgc();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = fgc();
}
return res * neg;
}
const int MO = 10007;
struct Matrix {
int m[3][3];
};
inline void mul(Matrix &a, Matrix b) {
Matrix res;
memset(res.m, 0, sizeof(res.m));
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
for(int k = 0; k < 3; k++) {
res.m[i][j] += a.m[i][k] * b.m[k][j];
res.m[i][j] %= MO;
}
}
}
a = res;
}
inline Matrix fpow(Matrix x, int k) {
if(k == 1) return x;
Matrix res = fpow(x, k >> 1);
mul(res, res);
if(k & 1) {
mul(res, x);
}
return res;
}
int T, n;
Matrix mat, def;
inline void init() {
memset(mat.m, 0, sizeof(mat.m));
mat.m[0][0] = 2;
mat.m[0][1] = 1;
mat.m[1][0] = mat.m[1][1] = mat.m[1][2] = 2;
mat.m[2][1] = 1;
mat.m[2][2] = 2;
memset(def.m, 0, sizeof(def.m));
def.m[0][0] = def.m[1][1] = def.m[2][2] = 1;
}
int main() {
T = readint();
while(T--) {
n = readint();
init();
mat = fpow(mat, n);
mul(def, mat);
printf("%d\n", def.m[0][0]);
}
return 0;
}