神奇的數據。開機20分鐘掛機4小時。
Build a tree
題意:
給一個完全k叉樹,rootn個節點點,問整個樹,每個節點的子節點數+1 異或出來的值是多少。思路:
其實不難發現,當有n層時,子樹裏會有n-1層的滿k叉樹x棵,n-2層的滿k叉樹y棵,n-1層的完全k叉樹z棵這三種情況。
如果k % 2 == 0,那麼對於一個滿k叉樹,他所異或的值爲整棵樹的節點數
如果k % 2 == 1,那麼多於一個滿k叉樹,他所異或的值爲從1到n層(1 - k ^ i) / (1 - k)的異或值
這個可以打個表。
當x % 2 == 1, y % 2 == 1的時候可以取值,因爲如果棵數成偶數的話就異或爲0了。我們可以打表算出,當層數爲n時擁有的結點數。
那麼我們可以通過剩下的rootn個結點,算出x,y,z。
然後遞歸直到爲0或爲1即可。
這裏要注意(1 - k ^ i) / (1 - k)這個不能直接算,k ^ i可能會先爆long long。累加即可。當k = 1時,樹爲鏈,求1-n的異或值,找規律發現4個出現一次0。那麼每次最多只需要算4次即可。
/*
滿足
n > (1 - k ^ a) / (1 - k)
有a + 1層,這層的總節點數爲(1 - k ^ (a + 1)) / (1 - k) = n1個節點
上一層總節點數爲(1 - k ^ a) / (1 - k) = n2 個節點
一棵滿k叉子樹需要(1 - k ^ a) / (1 - k) = n3個節點
一棵少一層的k叉子樹需要(1 - k ^ (a - 1)) / (1 - k) = n4個節點
那麼剩下的節點數爲z = n - x * n3 - y * n4
那麼可以算出
y有(n1 - n) / k ^ a個
x有(n - n2) / k ^ a個
可得剩下的節點數
有前綴和
1 異或 (1 - k ^ 2) / (1 - k) 異或 ...
可知花費爲
^rootn
x % 2 == 1時 貢獻 ^sum(a)
y % 2 == 1時 貢獻 ^sum(a - 1)
繼續遞歸可得
這裏n1可能已經爆long long了,所以可以通過其他方式計算到x,y
*/
#include <bits/stdc++.h>
#define ll long long
#define MAXN 200
using namespace std;
const ll MOD = 1e9;
ll f[MAXN], sum[MAXN];
ll mi[MAXN];
ll ans, k;
void dfs(ll rootn) {
ans ^= rootn;
if (rootn == 0 || rootn == 1) {
return ;
}
ll a = 1;
for (ll i = 1; i < MAXN; i++) {
if (f[i] >= rootn || f[i] <= 0 || mi[i] <= 0) {
break;
}
a = i;
}
ll n2 = f[a];
ll n3 = n2;
ll n4 = f[a - 1];
ll y;
ll x = (rootn - n2) / mi[a - 1];
if ((n2 - x * mi[a - 1]) % mi[a - 1] == 0) {
y = k - x;
} else {
y = k - x - 1;
y = y < 0 ? 0 : y;
}
if (x % 2) {
ans ^= sum[a];
}
if (y % 2) {
ans ^= sum[a - 1];
}
ll z = rootn - x * n3 - y * n4 - 1;
dfs(z);
}
void init() {
f[0] = sum[0] = 0;
mi[0] = 1;
for (ll i = 1; i < MAXN; i++) {
mi[i] = mi[i - 1] * k;
f[i] = mi[i - 1] + f[i - 1];
if (k & 1) {
sum[i] = sum[i - 1] ^ f[i];
} else {
sum[i] = f[i];
}
}
}
int main() {
int T;
ll n;
scanf("%d", &T);
while (T--) {
scanf("%lld %lld", &n, &k);
ans = 0;
if (k == 1) {
while (n && (n + 1) % 4) {
ans ^= n;
n--;
}
} else {
init();
dfs(n);
}
printf("%lld\n", ans);
}
}
/*
100
1000000000000000000 1000000000
*/
Euler theorem
簽到題。
題意:有a % b,現在告訴你a,問a % b的值有多少種情況出現。
思路:不難發現,大於0,小於(a + 1) / 2的數都可以被取得,再加上本身可以%=0
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
int T;
ll n;
scanf("%d", &T);
while (T--) {
scanf("%lld", &n);
printf("%lld\n" , (n + 1) / 2 + 1);
}
}
Free from square
題意:
從1到n當中,每次選取不超過k次,問能取到沒有平方因子的情況有多少種。思路:
沒有平方因子很容易能想到素數,也就是500以內的素數隨機組合選取,但是又要不超過k次。那就有點難度了。。
500的平方根以內的素數一共有8個,我們只要保證這八個不重複即可(因爲其他的只要重複了就超過了500了)。
先將1到n以內的數按照刨去這八個素數後所得的數分組。
每組裏面有着對於這八個數的不同狀態,狀壓一下。
將所有情況都分組後,就是分組揹包問題了。
dp的時候要每次枚舉不同的狀態和當前可以選擇的數目,從上一次的情況獲取到。
然後滾動數組維護一下即可。
#include <bits/stdc++.h>
#define MAXN 505
#define ll long long
#define MOD 1000000007
using namespace std;
int p[] = {2, 3, 5, 7, 11, 13, 17, 19};
vector<int> v[MAXN];
int b[MAXN], bel[MAXN];
int n, k;
bool mark[MAXN][1 << 9];
ll dp[2][MAXN][1 << 9];
void add(int x, int y) {
v[x].push_back(y);
}
void init() {
for (int i = 0; i < MAXN; i++) {
v[i].clear();
b[i] = 0;
bel[i] = i;
}
memset(dp, 0, sizeof(dp));
memset(mark, false, sizeof(mark));
}
void div() {
for (int i = 1; i <= n; i++) {
for (int j = 0; j < 8; j++) {
if (b[i] != -1 && i % p[j] == 0 && i % (p[j] * p[j]) != 0) {
b[i] |= (1 << j);
bel[i] /= p[j];
} else if (i % p[j] == 0 && i % (p[j] * p[j]) == 0) {
b[i] = -1;
break;
}
}
}
for (int i = 1; i <= n; i++) {
if (b[i] != -1) {
if (bel[i] == 1) {
add(i, b[i]);
} else {
add(bel[i], b[i]);
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j < v[i].size(); j++) {
mark[i][v[i][j]] = true;
}
}
}
void work() {
for (int i = 1; i <= n; i++) {
if (v[i].size() == 0) {
continue;
}
for (int j = 0; j < (1 << 8); j++) {
if (!mark[i][j]) {
continue;
}
dp[i & 1][1][j] = (dp[(i & 1) ^ 1][1][j] + 1) % MOD;
for (int l = 1; l <= k; l++) {
for (int q = 0; q < (1 << 8); q++) {
if (!(j & q)) {
dp[i & 1][l][j | q] = (dp[(i & 1) ^ 1][l - 1][q] + dp[i & 1][l][j | q]) % MOD;
}
}
}
}
for (int j = 1; j <= k; j++) {
for (int l = 0; l < (1 << 8); l++) {
dp[(i & 1) ^ 1][j][l] = dp[i & 1][j][l];
}
}
}
}
ll cal() {
ll ans = 0;
for (int i = 1; i <= k; i++) {
for (int j = 0; j < (1 << 8); j++) {
ans += dp[k & 1][i][j];
ans %= MOD;
}
}
return ans;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
init();
scanf("%d %d", &n, &k);
div();
work();
printf("%lld\n", cal());
}
}
Hard challenge
題意:
給n個座標點每個點的價值爲val,兩兩點構成的線段的價值爲val1 * val2,現在求一條經過(0,0)點的直線可以使它交到的線段價值最大。思路:
把所有點都打成以角度排序,以某點轉180°的點,都可以對該點造成價值。
用尺取法一段一段180°取就好了。
用x,y算它角度的 atan2(y, x) / PI * 180
#include <bits/stdc++.h>
#define ll long long
#define MAXN 100005
using namespace std;
const double PI = acos(-1.0);
struct point {
ll x, y, val;
double ang;
point(ll _x, ll _y, ll _val) : x(_x), y(_y), val(_val) {
ang = atan2(y, x) / PI * 180;
if (ang < 0) {
ang += 360;
}
}
point() {}
} reg[MAXN];
int cmp(point a, point b) {
return a.ang < b.ang;
}
int main() {
ll T, x, y, val, n;
scanf("%lld", &T);
while (T--) {
scanf("%lld", &n);
for (int i = 0; i < n; i++) {
scanf("%lld %lld %lld", &x, &y, &val);
reg[i] = point(x, y, val);
reg[i + n] = point(x, y, val);
reg[i + n].ang += 360;
}
n *= 2;
sort(reg, reg + n, cmp);
ll l, r;
l = 0, r = 0;
ll lval = 0, rval = 0;
while (r + 1 < n && reg[0].ang + 180 > reg[r + 1].ang) {
rval += reg[++r].val;
}
ll ans = 0;
if (r == 0) {
rval += reg[++r].val;
}
for (ll i = 1; i < n; i++) {
rval -= reg[i].val;
lval += reg[i - 1].val;
while (l < i && reg[i].ang - 180 >= reg[l].ang) {
lval -= reg[l++].val;
}
while (r + 1 < n && reg[i].ang + 180 > reg[r + 1].ang) {
rval += reg[++r].val;
}
ans = max(ans, (lval + reg[i].val) * rval);
if (i == r && i + 1 < n) {
rval += reg[++r].val;
}
}
printf("%lld\n", ans);
}
}
/*
3
3
1 1 1
1 -1 10
-1 0 100
*/
Just do it
題目:
給一個序列a,有一種操作,對於每一個下標i的數,他是[1,i]之間的ai異或得到的
現在進行m次操作,問最終序列的樣子。思路:
其實可以發現有dp[i][j]表示下標爲i,已經操作了j次。
有dp[i][j]=dp[i−1][j]xordp[i][j−1]
再轉化一下。有dp[i][j]=dp[i−2k]xordp[i][j−2k]
那可以發現,其實對於一個下標爲i的,我們只需要做他j二進制化後,爲1的位置(跟快速冪差不多)。
然後dp一下就好了。
#include<bits/stdc++.h>
#define MAXN 200005
#define ll long long
using namespace std;
ll a[MAXN];
int main(){
ll T, n, m;
scanf("%d", &T);
while (T--) {
scanf("%lld %lld", &n, &m);
for (ll i = 0; i < n; i++) {
scanf("%lld", &a[i]);
}
for (ll k = 0; 1 << k <= m; k++) {
if (m >> k & 1) {
for (ll i = 1 << k; i < n; i++) {
a[i] ^= a[i - (1 << k)];
}
}
}
for (ll i = 0; i < n; i++) {
if (i) {
printf(" ");
}
printf("%lld", a[i]);
}
puts("");
}
}
Kolakosk
題意:一個Kolakoski序列。。。。。。問你第n位是多少
什麼是Kolakoski序列呢。百度去吧。。好難描述思路:按照Kolakoski的描述打表即可。。
數據水到比賽的時候判奇偶都可以過。。
#include <bits/stdc++.h>
#define ll long long
#define MAXN 10000005
using namespace std;
int k[MAXN];
void init() {
k[1] = 1;
k[2] = 2;
int cnt = 2;
for (int i = 1; i < MAXN; i += k[cnt++]) {
for (int j = 0; j < k[cnt]; j++) {
k[i + j + 1] = k[i] ^ 3;
}
}
}
int main() {
int T, n;
init();
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
printf("%d\n", k[n]);
}
}