A
思路:公式推導就行了:
對於 ,只有都是1才爲1,所以統計這n個值中第k個二進制位1的個數cnt[k],後面一串就可以表示爲 C(cnt[k], 2)。
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子序列有多少個。
思路:動態規劃,對於子序列的動態規劃,思路也很常見。這裏定義 表示截止到第 個值,本質不同的長度爲 的子序列個數。
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
求 的解 x y z 都是正整數。。
思路:擴展歐幾里得算法。其實能夠推理出,要想有解,當且僅當 , 注意這裏解不一定都是正整數,還可能是包含負整數。
,如果 已知的話,這個就是二元不定方程,同時該方程有解,當且僅當 。
這裏令 , 。
移項得 ,是不是很眼熟?這個也是一個二元不定方程,同理要想有解,當且僅當 。
先解出第二個方程,然後將其帶回求解第一個方程。
注意:很重要的一點,因爲本題目求的解都是正整數,所以求解第一個不定方程時候,要儘可能讓 小, 儘可能大,這樣第一個方程才更容易求得正整數解(這一點也很容易想: 都是正整數 ,如果 很小的話,根本不可能有正整數解 的)。
補充知識:
如果一個二元不定方程 ,有特解 ,則通解爲
,
或
,
更詳細見代碼
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;
}