我不仅又变菜了,还学会了演戏
分析:每次贪心的取两个集合中异或值最小的一个pair,那么首先可以确定使用字典树来解决。比较容易想到用两颗字典树来维护,问题在于如何取最小值,因为不能确定两棵树同时走0还是同时走1。
其实只需要一起走就可以了,因为同一个节点的0和1是相互不影响的,那么从0向下递归,与从1向下递归,他们的结果也是不相互影响的,所以只需要把整棵树跑完最后在排个序就可以了。
那么最后的做法就是同步的跑两棵树,优先跑00和11,再跑01和10。
#include "bits/stdc++.h"
using namespace std;
struct node {
int nxt[2], num;
} a[100004 * 32], b[100004 * 32];
int cnt1, cnt2;
int ans[100004];
int cnt;
void init() {
cnt1 = cnt2 = cnt = 0;
a[0].nxt[0] = a[0].nxt[1] = b[0].nxt[0] = b[0].nxt[1] = -1;
a[0].num = b[0].num = 0;
}
void ins(node t[], char s[], int root, int &cnt) {
int len = strlen(s);
for (int i = 0; i < len; ++i) {
if (t[root].nxt[s[i] - '0'] == -1) {
t[root].nxt[s[i] - '0'] = ++cnt;
root = cnt;
t[root] = {{-1, -1}, 0};
} else root = t[root].nxt[s[i] - '0'];
t[root].num++;
}
}
int qu(int rt1, int rt2, int w, int base) {
bool ok = 1;
int res = 0;
while ((~a[rt1].nxt[0]) && (~b[rt2].nxt[0])) {
int temp = qu(a[rt1].nxt[0], b[rt2].nxt[0], w, base >> 1);
res += temp;
a[rt1].num -= temp;
b[rt2].num -= temp;
int nx1 = a[rt1].nxt[0], nx2 = b[rt2].nxt[0];
if (a[nx1].num == 0)
a[rt1].nxt[0] = -1;
if (b[nx2].num == 0)
b[rt2].nxt[0] = -1;
ok = 0;
}
while ((~a[rt1].nxt[1]) && (~b[rt2].nxt[1])) {
int temp = qu(a[rt1].nxt[1], b[rt2].nxt[1], w, base >> 1);
res += temp;
a[rt1].num -= temp;
b[rt2].num -= temp;
int nx1 = a[rt1].nxt[1], nx2 = b[rt2].nxt[1];
if (a[nx1].num == 0)
a[rt1].nxt[1] = -1;
if (b[nx2].num == 0)
b[rt2].nxt[1] = -1;
ok = 0;
}
while ((~a[rt1].nxt[0]) && (~b[rt2].nxt[1])) {
int temp = qu(a[rt1].nxt[0], b[rt2].nxt[1], w + base, base >> 1);
res += temp;
a[rt1].num -= temp;
b[rt2].num -= temp;
int nx1 = a[rt1].nxt[0], nx2 = b[rt2].nxt[1];
if (a[nx1].num == 0)
a[rt1].nxt[0] = -1;
if (b[nx2].num == 0)
b[rt2].nxt[1] = -1;
ok = 0;
}
while ((~a[rt1].nxt[1]) && (~b[rt2].nxt[0])) {
int temp = qu(a[rt1].nxt[1], b[rt2].nxt[0], w + base, base >> 1);
res += temp;
a[rt1].num -= temp;
b[rt2].num -= temp;
int nx1 = a[rt1].nxt[1], nx2 = b[rt2].nxt[0];
if (a[nx1].num == 0)
a[rt1].nxt[1] = -1;
if (b[nx2].num == 0)
b[rt2].nxt[0] = -1;
ok = 0;
}
if (ok) {
ans[cnt++]=w;
res++;
a[rt1].num -= res;
b[rt2].num -= res;
}
return res;
}
char s[35];
int main() {
int t;cin>>t;
while (t--) {
int n,x;cin>>n;
init();
for (int i = 0; i < n; ++i) {
scanf("%d",&x);
for (int j = 30; j >= 0; --j) {
s[j] = '0' + (x & 1);
x >>= 1;
}
s[31] = '\0';
ins(a, s, 0, cnt1);
}
for (int i = 0; i < n; ++i) {
scanf("%d",&x);
for (int j = 30; j > 0; --j) {
s[j] = '0' + (x & 1);
x >>= 1;
}
s[31] = '\0';
ins(b, s, 0, cnt2);
}
qu(0, 0, 0, 1 << 30);
sort(ans,ans+cnt);
for (int i = 0; i < cnt; ++i) {
printf("%d%c", ans[i], (i == cnt - 1 ? '\n' : ' '));
}
}
}
分析:这是一个分段函数,有n+1段,然后分别算一下每段函数的解是否存在就可以了。写了一年的bug
#include "bits/stdc++.h"
using namespace std;
struct node {
int up, dw;
bool friend operator<(node a, node b) {
node t1 = a;
node t2 = b;
if (t1.dw < 0) {
t1.up *= -1;
t1.dw *= -1;
}
if (t2.dw < 0) {
t2.up *= -1;
t2.dw *= -1;
}
long long tt1 = (long long) t1.up * t2.dw;
long long tt2 = (long long) t2.up * t1.dw;
return tt1 < tt2;
}
bool friend operator==(node a, node b) {
long long t1 = (long long) a.up * b.dw;
long long t2 = (long long) b.up * a.dw;
return t1 == t2;
}
};
vector v, ans;
int a[100004], b[100004];
int main() {
int T;
cin >> T;
while (T--) {
int n, c;
scanf("%d%d", &n, &c);
v.clear();
ans.clear();
int A = 0, B = 0;
for (int i = 1; i <= n; ++i) {
scanf("%d%d", &a[i], &b[i]);
v.push_back({-b[i], a[i]});
A += -a[i];
B += -b[i];
}
B -= c;
sort(v.begin(), v.end());
bool ok = 0;
for (int i = 0; i < v.size() && !ok; ++i) {
if (A == 0 && B == 0)ok = 1;
else if (A == 0 && B != 0) {
A += v[i].dw * 2;
B -= v[i].up * 2;
continue;
} else {
node t;
if (B == 0) {
t = {0, 1};
} else {
int g = __gcd(A, B);
t = {-B / g, A / g};
}
node s = {v[i].up, v[i].dw};
node las;
if (i != 0)las = {v[i - 1].up, v[i - 1].dw};
if (i == 0 && (t < s || t == s)) {
ans.push_back(t);
} else if ((t < s || t == s) && (las < t || las == t)) {
ans.push_back(t);
}
A += v[i].dw * 2;
B -= v[i].up * 2;
}
}
node t;
if (A == 0 && B == 0)ok = 1;
else if (A == 0 && B != 0);
else if (B == 0) {
t = {0, 1};
if (v[v.size() - 1] < t || v[v.size() - 1] == t)ans.push_back(t);
} else {
int g = __gcd(abs(A), abs(B));
t = {-B / g, A / g};
if (v[v.size() - 1] < t || v[v.size() - 1] == t)ans.push_back(t);
}
sort(ans.begin(), ans.end());
ans.erase(unique(ans.begin(), ans.end()), ans.end());
if (ok) {
puts("-1");
} else {
printf("%d", ans.size());
printf("%c", ans.size() == 0 ? '\n' : ' ');
for (int i = 0; i < ans.size(); ++i) {
if (ans[i].dw < 0) {
ans[i].dw *= -1;
ans[i].up *= -1;
}
printf("%d/%d%c", ans[i].up, ans[i].dw, (i == ans.size() - 1 ? '\n' : ' '));
}
}
}
}
分析:首先很容易得到字典序最小的前两位是n,1。那么若(n-2)!>=k前两个就是n,1。那么对于n<=9的预处理打表,n>9的就可以线性推出来了,首先n>9的前两位肯定是n,1。那么按照字典序的关系,第三位的数应该是递增的,并且第三位的数对于的个数应该是fac[3],那么此时比较一下k与fac[n-3]的关系,直接从小到大枚举一下就可以了。
#include "bits/stdc++.h"
using namespace std;
long long fac[24];
int ans[24];
bool vis[24];
int init[10][10004][10];
struct node {
int d[34];
int x[34];
int n;
bool friend operator<(node a, node b) {
for (int i = 1; i < a.n; ++i) {
if (a.d[i] < b.d[i])return 1;
if (a.d[i] > b.d[i])return 0;
}
return 0;
}
} a[1000000];
vector v;
int cnt;
int main() {
fac[0] = 1;
v.resize(30);
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= 20; ++i) {
fac[i] = fac[i - 1] * i;
}
v[1] = 1;
for (int i = 2; i <= 9; ++i) {
v[i] = i;
for (int j = 1; j <= fac[i]; ++j) {
for (int k = 1; k < i; ++k) {
a[j].d[k] = v[k + 1] - v[k];
}
for (int k = 1; k <= i; ++k) {
a[j].x[k] = v[k];
}
a[j].n = i;
next_permutation(v.begin() + 1, v.begin() + i + 1);
}
sort(a + 1, a + fac[i] + 1);
for (int k = 1; k <= min(fac[i], 10000LL); ++k) {
for (int j = 1; j <= i; ++j) {
init[i][k][j] = a[k].x[j];
}
}
}
int t;
cin >> t;
while (t--) {
int n, k;
scanf("%d%d", &n, &k);
if (n > 9) {
memset(vis, 0, sizeof(vis));
ans[1] = n;
ans[2] = 1;
vis[n] = 1;
vis[1] = 1;
for (int i = 3; i <= n; ++i) {
int base;
for (int j = 1; j <= n; ++j) {
if (!vis[j]) {
base = j;
break;
}
}
while (k > fac[n - i]) {
k -= fac[n - i];
base++;
while (vis[base])base++;
}
ans[i] = base;
vis[base] = 1;
}
for (int i = 1; i <= n; ++i) {
printf("%d%c", ans[i], (i == n ? '\n' : ' '));
}
} else {
for (int i = 1; i <= n; ++i) {
printf("%d%c", init[n][k][i], (i == n ? '\n' : ' '));
}
}
}
}
exkmp板子。累加一下算个贡献就可以了,答案加上每个位置的lcp,并且对于每位check是否匹配到最后一位,否则答案+1。
#include
using namespace std;
void EX_ten(char *a, char *b, int M, int N, long long *Next, long long *ret) {
int i, j, k;
for (j = 0; 1 + j < M && a[j] == a[1 + j]; j++);
Next[1] = j;
k = 1;
for (i = 2; i < M; i++) {
int Len = k + Next[k], L = Next[i - k];
if (L < Len - i) {
Next[i] = L;
} else {
for (j = max(0, Len - i); i + j < M && a[j] == a[i + j]; j++);
Next[i] = j;
k = i;
}
}
for (j = 0; j < N && j < M && a[j] == b[j]; j++);
ret[0] = j;
k = 0;
for (i = 1; i < N; i++) {
int Len = k + ret[k], L = Next[i - k];
if (L < Len - i) {
ret[i] = L;
} else {
for (j = max(0, Len - i); j < M && i + j < N && a[j] == b[i + j]; j++);
ret[i] = j;
k = i;
}
}
}
char s1[1000005], s2[1000005];
long long ret[1000005];
long long Next[1000005];
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%s", s1);
int n = strlen(s1);
for (int i = 0; i <= n; i++) ret[i] = Next[i] = 0;
for (int i = 0; i <= n; i++) s2[i] = s1[i];
EX_ten(s1, s2, n, n, Next, ret);
long long ans = 0;
for (int i = 1; i < n; i++) {
ans += ret[i];
ans += (ret[i] != n - i);
}
printf("%lld\n", ans);
}
return 0;
}
分析:容易得到n,1,n的数据是一个递推式F[N]=F[N-1]+F[N-3],同时还能得到答案和x,y与1,n的差值有关。x可以走到x-2,x-4……1,3……x-3,x-1,即对于样例12 3 9,相当于[4,8],即5 1 5,注意特判一下x=1和y=n就可以了。
#include "bits/stdc++.h"
using namespace std;
const int mod = 998244353;
long long f[100004];
int main() {
int t;
f[0] = 0;
f[1] = 1;
f[2] = 1;
for (int i = 3; i < 100004; ++i) {
f[i] = f[i - 1] + f[i - 3];
f[i] %= mod;
}
cin >> t;
int n, x, y;
while (t--) {
scanf("%d%d%d", &n, &x, &y);
int dx = x - 1, dy = n - y;
int pos = n;
if (dx == 0 && dy == 0) {
printf("%lld\n", f[pos]);
} else {
if (dx)pos -= 1;
if (dy)pos -= 1;
pos -= dx;
pos -= dy;
pos = max(0, pos);
if ((x == 1 || y == n) && y - x == 1) {
puts("1");
} else printf("%lld\n", f[pos]);
}
}
}