整除序列
题目大意
有一个序列,序列的第一个数是
n
n
n,后面的每个数是前一个数整除 2,请输出这个序列中值为正数的项。
输入格式
输入一行包含一个整数
n
n
n。
输出格式
输出一行,包含多个整数,相邻的整数之间用一个空格分隔,表示答案。
数据范围:
1
≤
n
≤
1
0
18
1≤n≤10^{18}
1≤n≤1018
输入样例
20
输出样例
20 10 5 2 1
将 n n n 大于 0 的数存入 vector,最后再输出 vector 内的内容。
#include <cstdio>
#include <iostream>
#include <vector>
#define pb push_back
using namespace std;
typedef long long ll;
vector<ll>v;
int main()
{
ll n;
scanf("%lld",&n);
v.pb(n);
while(n)
{
n >>= 1;
if(n) v.pb(n);
}
for(auto i : v) printf("%lld ",i);
return 0;
}
解码
题目大意
小明有一串很长的英文字母,可能包含大写和小写。
在这串字母中,有很多连续的是重复的。
小明想了一个办法将这串字母表达得更短:将连续的几个相同字母写成字母 + 出现次数的形式。
例如,连续的 5 个
a
a
a,即
a
a
a
a
a
aaaaa
aaaaa,小明可以简写成
a
5
a5
a5(也可能简写成
a
4
a
a4a
a4a、
a
a
3
a
aa3a
aa3a 等)。
对于这个例子:
H
H
H
e
l
l
l
l
l
o
o
HHHellllloo
HHHellllloo,小明可以简写成
H
3
e
l
5
o
2
H3el5o2
H3el5o2。
为了方便表达,小明不会将连续的超过 9 个相同的字符写成简写的形式。
现在给出简写后的字符串,请帮助小明还原成原来的串。
输入格式
输入一行包含一个字符串。
输出格式
输出一个字符串,表示还原后的串。
数据范围:输入字符串由大小写英文字母和数字组成,长度不超过 100。
请注意原来的串长度可能超过 100。
输入样例
H3el5o2
输出样例
HHHellllloo
遇到数字 n u m num num,存储 n u m num num - 1 个 前一个字符,最后输出 v e c t o r vector vector 存储的数据。
#include <cstdio>
#include <vector>
#include <cstring>
#define pb push_back
using namespace std;
char s[110];
vector<char> v;
int main()
{
scanf("%s",s);
int len = strlen(s);
for(int i = 0; i < len; i++)
{
if(s[i] >= '0' && s[i] <= '9')
{
int t = s[i] - '0';
while(t > 1) v.pb(s[i-1]), t--;
}
else v.pb(s[i]);
}
for(auto &i : v) printf("%c",i);
return 0;
}
走方格
题目大意
在平面上有一些二维的点阵。
这些点的编号就像二维数组的编号一样,从上到下依次为第 1 至第
n
n
n 行,从左到右依次为第 1 至第
m
m
m 列,每一个点可以用行号和列号来表示。
现在有个人站在第 1 行第 1 列,要走到第
n
n
n 行第
m
m
m 列。
只能向右或者向下走。
注意,如果行号和列数都是偶数,不能走入这一格中。
问有多少种方案。
输入格式
输入一行包含两个整数
n
,
m
n,m
n,m。
输出格式
输出一个整数,表示答案。
数据范围:
1
≤
n
,
m
≤
30
1≤n,m≤30
1≤n,m≤30
输入样例
3 4
输出样例
2
f
[
i
]
[
j
]
f[i][j]
f[i][j]:表示在第
i
i
i 行 第
j
j
j 列
属性就是方案数
f
[
i
]
[
j
]
f[i][j]
f[i][j] 可以由
f
[
i
−
1
]
[
j
]
f[i-1][j]
f[i−1][j] 或者
f
[
i
]
[
j
−
1
]
f[i][j-1]
f[i][j−1]走过来
i
i
i%2 == 0 &&
j
j
j%2 == 0 表示不可以走
最后输出方案数
f
[
n
]
[
m
]
f[n][m]
f[n][m]
#include <cstdio>
using namespace std;
int f[35][35];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
f[1][1] = 1;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
if(i%2 == 0 && j%2 == 0) continue;
f[i][j] += f[i-1][j] + f[i][j-1];
}
}
printf("%d\n",f[n][m]);
return 0;
}
整数拼接
题目大意
给定一个长度为
n
n
n 的数组
A
1
,
A
2
,
⋅
⋅
⋅
,
A
n
A_1,A_2,⋅⋅⋅,A_n
A1,A2,⋅⋅⋅,An。
你可以从中选出两个数
A
i
A_i
Ai 和
A
j
A_j
Aj(
i
i
i 不等于
j
j
j),然后将
A
i
A_i
Ai 和
A
j
A_j
Aj 一前一后拼成一个新的整数。
例如 12 和 345 可以拼成 12345 或 34512。
注意交换
A
i
A_i
Ai 和
A
j
A_j
Aj 的顺序总是被视为 2 种拼法,即便是
A
i
=
A
j
A_i=A_j
Ai=Aj 时。
请你计算有多少种拼法满足拼出的整数是
K
K
K 的倍数。
输入格式
第一行包含 2 个整数
n
n
n 和
K
K
K。
第二行包含
n
n
n 个整数
A
1
,
A
2
,
⋅
⋅
⋅
,
A
n
A_1,A_2,⋅⋅⋅,A_n
A1,A2,⋅⋅⋅,An。
输出格式
一个整数代表答案。
数据范围:
1
≤
n
≤
1
0
5
,
1
≤
K
≤
1
0
5
,
1
≤
A
i
≤
1
0
9
1≤n≤10^5,1≤K≤10^5,1≤A_i≤10^9
1≤n≤105,1≤K≤105,1≤Ai≤109
输入样例
4 2
1 2 3 4
输出样例
6
暴力代码 O ( n 2 ) O(n^2) O(n2)
ll ans = 0;
ll num[11]={0,1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
{
if(i!=j)
{
int leni = get_len(a[i]), lenj = get_len(a[j]);
ll t = a[i]*num[lenj]+a[j];
if(t%k==0) ans++;
t = a[j]*num[leni]+a[i];
if(t%k==0) ans++;
}
}
所谓的优化就是空间换时间,外层循环无法去掉,那么我们就想办法去掉内层循环的东西。
a
[
i
]
g
e
t
l
e
n
(
a
[
j
]
)
a[i]^{getlen(a[j])}
a[i]getlen(a[j])+
a
[
j
]
a[j]
a[j]
拆成两部分 a[i]*10^(getlen(a[j])) 和 a[j]
(k-a[j]%k)%k //放在后面符合拼接数的个数
for(int t = 1, j = 0; j <= 10; j++)
{
cnt[j][a[i]*t%k]++; //放在前面
t = t*10%k;
}
j <= 10 是因为 get_len获取的长度1e9是10位,所以 j 要遍历完二维维所有数据需要注意 j >= 10,但是也不能太大。然后翻转数组再求一次就是我们最终需要的结果
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
ll a[maxn], n, k, ans = 0;
ll cnt[15][maxn]; //cnt[i][j]存乘以10^i%k余数是j的数
int get_len(int x) //获取字符长度
{
int res = 0;
while(x)
{
res++;
x /= 10;
}
return res;
}
void solve()
{
memset(cnt, 0 ,sizeof cnt); //清0
for(int i = 0; i < n; i++)
{
ans += cnt[get_len(a[i])][(k - a[i]%k)%k]; //a[i]放在后面
for(int t = 1,j = 0; j < 11; j++)
{
cnt[j][t*a[i]%k]++; //a[i]放在前面
t = t*10%k;
}
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i = 0; i < n; i++) scanf("%lld",&a[i]);
solve();
reverse(a,a+n);
solve();
printf("%lld\n",ans);
return 0;
}
网络分析
题目大意
小明正在做一个网络实验。
他设置了
n
n
n 台电脑,称为节点,用于收发和存储数据。
初始时,所有节点都是独立的,不存在任何连接。
小明可以通过网线将两个节点连接起来,连接后两个节点就可以互相通信了。
两个节点如果存在网线连接,称为相邻。
小明有时会测试当时的网络,他会在某个节点发送一条信息,信息会发送到每个相邻的节点,之后这些节点又会转发到自己相邻的节点,直到所有直接或间接相邻的节点都收到了信息。
所有发送和接收的节点都会将信息存储下来。
一条信息只存储一次。
给出小明连接和测试的过程,请计算出每个节点存储信息的大小。
输入格式
输入的第一行包含两个整数
n
,
m
n,m
n,m,分别表示节点数量和操作数量。
节点从 1 至
n
n
n 编号。
接下来
m
m
m 行,每行三个整数,表示一个操作。
如果操作为
1
1
1
a
a
a
b
b
b,表示将节点
a
a
a 和节点
b
b
b 通过网线连接起来。当
a
=
b
a = b
a=b 时,表示连接了一个自环,对网络没有实质影响。
如果操作为
2
p
t
2 p t
2pt,表示在节点
p
p
p 上发送一条大小为
t
t
t 的信息。
输出格式
输出一行,包含
n
n
n 个整数,相邻整数之间用一个空格分割,依次表示进行完上述操作后节点 1 至节点
n
n
n 上存储信息的大小。
数据范围:
1
≤
n
≤
10000
,
1
≤
m
≤
105
,
1
≤
t
≤
100
1≤n≤10000,1≤m≤105,1≤t≤100
1≤n≤10000,1≤m≤105,1≤t≤100
输入样例
4 8
1 1 2
2 1 10
2 3 5
1 4 1
2 2 2
1 1 2
1 2 4
2 2 1
输出样例
13 13 5 3
并查集
带权并查集
d
[
i
]
d[i]
d[i]存储的是第
i
i
i 个点到根节点的差值
w
[
i
]
w[i]
w[i]存储的是第
i
i
i 个点到根节点的总的信息量
每次合并的时候维护一下
d
[
]
d[]
d[] 数组,最后求一下前缀和即可
#include <cstdio>
using namespace std;
const int maxn = 4e4 + 7;
int fa[maxn];
int d[maxn], w[maxn];
int res[maxn];
void init() //初始化
{
for(int i = 1; i < maxn; i++) fa[i] = i;
}
int find(int x) //路径压缩,找打 x 的根结点
{
if(fa[x] != x)
{
int root = find(fa[x]);
d[x] += d[fa[x]]; //当前点 x 到 fa[x]的距离
fa[x] = fa[root];
}
return fa[x];
}
void Merge(int x,int y) //合并, y代表的集合 作为 x 集合的根节点
{
fa[find(x)] = find(y);
}
int main()
{
int n,m;
init();
scanf("%d%d",&n,&m);
while(m--)
{
int op, x, y;
scanf("%d%d%d",&op,&x,&y);
if(op == 1)
{
int px = find(x), py = find(y);
if(px == py) continue; //已经是一个集合
Merge(x,y); //合并
d[px] += w[px] - w[py]; //减去合并之前根结点上的权值
}
if(op == 2) w[find(x)] += y; //x 的根结点加上 y
}
for(int i = 1; i<= n; i++) res[i] = w[find(i)] + d[i]; //前缀和
for(int i = 1; i<= n; i++) printf("%d ",res[i]);
return 0;
}
超级胶水
题目大意
小明有
n
n
n 颗石子,按顺序摆成一排。
他准备用胶水将这些石子粘在一起。
每颗石子有自己的重量,如果将两颗石子粘在一起,将合并成一颗新的石子,重量是这两颗石子的重量之和。
为了保证石子粘贴牢固,粘贴两颗石子所需要的胶水与两颗石子的重量乘积成正比,本题不考虑物理单位,认为所需要的胶水在数值上等于两颗石子重量的乘积。
每次合并,小明只能合并位置相邻的两颗石子,并将合并出的新石子放在原来的位置。
现在,小明想用最少的胶水将所有石子粘在一起,请帮助小明计算最少需要多少胶水。
输入格式
输入的第一行包含一个整数
n
n
n,表示初始时的石子数量。
第二行包含
n
n
n 个整数
w
1
,
w
2
,
…
,
w
n
w_1,w_2,…,w_n
w1,w2,…,wn,依次表示每颗石子的重量。
输出格式
一个整数表示答案。
数据范围:
1
≤
n
≤
1
0
5
,
1
≤
w
i
≤
1000
1≤n≤10^5,1≤w_i≤1000
1≤n≤105,1≤wi≤1000
输入样例
3
3 4 5
输出样例
47
设总花费为
f
f
f
当
n
n
n = 2 时,设石头重量分别为
a
,
b
a,b
a,b,则
f
=
a
∗
b
f=a*b
f=a∗b 正确;
当
n
n
n = 3 时,设石头重量分别为
a
,
b
,
c
a,b,c
a,b,c
则所有合并方式分别为:
f
1
=
a
∗
b
+
(
a
+
b
)
∗
c
=
a
∗
b
+
a
∗
c
+
b
∗
c
f_1=a*b+(a+b)*c=a*b+a*c+b*c
f1=a∗b+(a+b)∗c=a∗b+a∗c+b∗c;
f
2
=
a
∗
c
+
(
a
+
c
)
∗
b
=
a
∗
b
+
a
∗
c
+
b
∗
c
f_2=a*c+(a+c)*b=a*b+a*c+b*c
f2=a∗c+(a+c)∗b=a∗b+a∗c+b∗c;
f
3
=
b
∗
c
+
(
b
+
c
)
∗
a
=
a
∗
b
+
a
∗
c
+
b
∗
c
f_3=b*c+(b+c)*a=a*b+a*c+b*c
f3=b∗c+(b+c)∗a=a∗b+a∗c+b∗c;
f
=
f
1
=
f
2
=
f
3
f=f_1=f_2=f_3
f=f1=f2=f3,正确。
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
int a[maxn];
int main()
{
int n;
cin>>n;
for(int i = 0; i < n; i++) cin>>a[i];
ll ans = 0;
ll t = (ll)a[0];
for(int i = 1; i < n; i++)
{
ll temp = t*(ll)a[i];
ans += temp;
t += a[i];
}
cout<<ans<<'\n';
return 0;
}