A
思路:公式推导就行了:
∑
i
=
1
n
∑
j
=
1
n
(
a
i
&
a
j
)
=
∑
i
=
1
n
∑
j
=
1
n
∑
k
=
0
30
2
k
a
i
k
a
j
k
=
∑
k
=
0
30
2
k
∑
i
=
1
n
∑
j
=
1
n
a
i
k
a
j
k
\sum_{i=1}^n\sum_{j=1}^n(a_i\&a_j) = \sum_{i=1}^n\sum_{j=1}^n\sum_{k=0}^{30}2^ka_{ik}a_{jk} = \sum_{k=0}^{30}2^k\sum_{i=1}^n\sum_{j=1}^na_{ik}a_{jk}
i=1∑nj=1∑n(ai&aj)=i=1∑nj=1∑nk=0∑302kaikajk=k=0∑302ki=1∑nj=1∑naikajk
对于
a
i
k
a
j
k
a_{ik}a_{jk}
aikajk ,只有都是1才为1,所以统计这n个值中第k个二进制位1的个数cnt[k],后面一串就可以表示为 C(cnt[k], 2)。
=
∑
k
=
0
30
2
k
(
c
n
t
[
k
]
∗
(
c
n
t
[
k
]
−
1
)
2
)
= \sum_{k=0}^{30}2^k (\frac{cnt[k] * (cnt[k]- 1)}{2})
=k=0∑302k(2cnt[k]∗(cnt[k]−1))
Coding:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 11;
const int M = 1e6 + 11;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int cnt[31];
int main(int argc, char **args){
int n;cin >>n;
for(int i = 0; i < n ; i++){
ll a; scanf("%lld", &a);
for(int j = 30; j >= 0; j--){
if(a >> j & 1)
cnt[j]++;
}
}
ll sum = 0 ;
for(int i = 30; i >= 0; i--){
sum += (1 << i) * 1ll *(cnt[i] * 1ll * (cnt[i]) );
}
cout << sum <<"\n";
return 0;
}
B
思路:对于每条边,它可以包括在n-2个三角形中,这样算下来,是比答案多了一倍,除以2就行了,不过这里是乘2的逆元。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 11;
const int M = 1e6 + 11;
const int MOD = 998244353;
ll _pow(ll a, ll b, ll c ){
ll s = 1, base = a % c;
while(b){
if(b & 1) s = (s * base) % c;
b >>= 1;
base = base * base % c;
}
return s;
}
int main(int argc, char **args){
int n; cin >> n;
vector<ll> x(n + 1), y(n + 1);
for(int i = 1; i <= n; i++){
scanf("%lld%lld", &x[i], &y[i]);
}
ll sum = 0;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n ;j ++){
sum += (abs(x[i] - x[j]) + abs(y[i] - y[j])) % MOD * (n - 2) % MOD;
sum %= MOD;
}
}
cout << sum * _pow(2, MOD - 2, MOD)% MOD<<"\n";
return 0;
}
/*
4
0 0
1 0
0 1
1 1
*/
C
问题:求长度为n序列中,本质不同的长度为k子序列有多少个。
思路:动态规划,对于子序列的动态规划,思路也很常见。这里定义
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示截止到第
i
i
i 个值,本质不同的长度为
j
j
j 的子序列个数。
51Nod 上有一个动态规划,是求长度为n序列中,本质不同的子序列有多少个。和这道题有异曲同工之妙。
Coding:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e3 + 11;
const int M = 1e6 + 11;
const int MOD = 1e9 + 7;
int dp[N][N], last[N]; // last[i] 表示到目前为止,i 最后一次出现的位置。
char s[N];
int main(int argc, char **args){
int n, k; cin >> n >> k;
scanf("%s" , s + 1);
int len = strlen(s + 1);
for(int i = 0; i <= len; i++) dp[i][0] = 1;
for(int i = 1; i <= len; i++){
for(int j = 1; j <= k; j++){
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];
if(last[s[i]] ){ // 去重复元素
dp[i][j] -= dp[last[s[i]] - 1][j - 1];
}
dp[i][j] = (dp[i][j] % MOD + MOD) % MOD;
}
last[s[i]] = i;
}
cout << dp[len][k] <<"\n";
return 0;
}
D
求
a
∗
x
+
b
∗
y
+
c
∗
z
=
k
a * x + b * y + c * z = k
a∗x+b∗y+c∗z=k 的解
(
x
,
y
,
z
)
(x, y, z)
(x,y,z) x y z 都是正整数。。
思路:扩展欧几里得算法。其实能够推理出,要想有解,当且仅当
g
c
d
(
a
,
b
,
c
)
∣
k
gcd(a, b, c ) | k
gcd(a,b,c)∣k, 注意这里解不一定都是正整数,还可能是包含负整数。
a
∗
x
+
b
∗
y
=
k
−
c
∗
z
a * x + b * y = k - c * z
a∗x+b∗y=k−c∗z,如果
z
z
z 已知的话,这个就是二元不定方程,同时该方程有解,当且仅当
g
c
d
(
a
,
b
)
∣
(
k
−
c
∗
z
)
gcd(a, b) | (k - c * z)
gcd(a,b)∣(k−c∗z) 。
这里令
d
=
g
c
d
(
a
,
b
)
d = gcd(a, b)
d=gcd(a,b),
k
−
c
∗
z
=
m
∗
d
,
(
m
∈
Z
+
)
k - c * z = m * d , (m\in Z^+)
k−c∗z=m∗d,(m∈Z+)。
移项得
m
∗
d
+
c
∗
z
=
k
,
(
z
∈
Z
+
,
m
∈
Z
+
)
m * d + c * z = k, (z \in Z^+, m \in Z^+)
m∗d+c∗z=k,(z∈Z+,m∈Z+),是不是很眼熟?这个也是一个二元不定方程,同理要想有解,当且仅当
g
c
d
(
d
,
c
)
∣
k
=
>
g
c
d
(
a
,
b
,
c
)
∣
k
gcd(d, c) | k =>gcd(a,b,c) |k
gcd(d,c)∣k=>gcd(a,b,c)∣k。
先解出第二个方程,然后将其带回求解第一个方程。
注意:很重要的一点,因为本题目求的解都是正整数,所以求解第一个不定方程时候,要尽可能让 z z z 小, m m m 尽可能大,这样第一个方程才更容易求得正整数解(这一点也很容易想: a , b a,b a,b 都是正整数 ,如果 m ∗ d m *d m∗d 很小的话,根本不可能有正整数解 ( x , y ) (x ,y) (x,y)的)。
补充知识:
如果一个二元不定方程
a
∗
x
+
b
∗
y
=
c
a * x + b * y = c
a∗x+b∗y=c,有特解
(
x
0
,
y
0
)
(x_0 , y_0)
(x0,y0),则通解为
x
=
x
0
+
b
/
g
c
d
(
a
,
b
)
∗
t
x = x_0 + b / gcd(a, b) * t
x=x0+b/gcd(a,b)∗t
y
=
y
0
−
b
/
g
c
d
(
a
,
b
)
∗
t
y = y_0 - b / gcd(a, b) * t
y=y0−b/gcd(a,b)∗t ,
(
t
>
=
0
)
(t >= 0)
(t>=0)
或
x
=
x
0
−
b
/
g
c
d
(
a
,
b
)
∗
t
x = x_0 - b / gcd(a, b) * t
x=x0−b/gcd(a,b)∗t
y
=
y
0
+
b
/
g
c
d
(
a
,
b
)
∗
t
y = y_0 + b / gcd(a, b) * t
y=y0+b/gcd(a,b)∗t ,
(
t
>
=
0
)
(t >= 0)
(t>=0)
更详细见代码
Coding:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e3 + 11;
const int M = 1e6 + 11;
const int MOD = 1e9 + 7;
ll exgcd(ll a, ll b, ll &x, ll &y){
if(!b) {
x = 1; y = 0;
return a;
}
ll xx, yy;
ll g = exgcd(b, a % b, x, y);
ll tmp = x;
x = y;
y = tmp - (a / b) * y;
return g;
}
ll solve (ll a, ll b, ll c, ll &x, ll &y){
ll g = exgcd(a, b, x, y);
x = x * (c / g); y = y * (c / g);
if(x < 0){ // 如果x < 0, 那么y一定特别大。所以就将x 增加到 尽可能小的正数,那么 y 也会降低一些,但肯定还会是正数, 因为 c 为正数。
ll t = ceil(-x * 1.0 / (b / g));
x = x + b / g * t;
y = y - a / g * t;
}
if(y < 0){// 同理
ll t = ceil(-y * 1.0 / (a / g));
y = y + a / g * t;
x = x - b / g * t;
}
return g;
}
ll solve_1(ll a, ll b, ll c, ll &x, ll &y){
ll g = exgcd(a, b, x, y);
x = x * (c / g); y = y * (c / g);
if(y < 0){ // 让 z 尽可能小,这样 m 就尽可能大了
ll t = ceil(-y * 1.0 / (a / g));
y = y + a / g * t;
x = x - b / g * t;
}
return g;
}
int main(int argc, char **args){
// freopen("in.txt", "r", stdin);
ll a, b, c, k;
cin >> a >> b >> c >> k;
ll x, y, z, m;
ll d = __gcd(a, b);
ll g = solve_1(d, c, k, m, z);
solve(a, b, m * d, x, y);
cout << x << " " << y <<" " << z<<"\n";
return 0;
}