补题补题
##Is Derek lying?##
题意:
Derek和Alfia分别做题,题目选项有A、B、C,现在他们所选的选项和所得的分告诉你,问他们有没有撒谎。
思路:判定他们的上下限即可,当D和A有一个人全对时,他们一样的肯定得分,不一样的肯定不得分,假设一样的选项为k个,不一样的为p个,那么他们总分之和一定小于k * 2 + p,两分之差肯定小于p。
#include <bits/stdc++.h>
#define MAXN 800050
using namespace std;
int main() {
int T, n, x, y;
char ch1[MAXN], ch2[MAXN];
scanf("%d", &T);
while (T--) {
scanf("%d %d %d", &n, &x, &y);
getchar();
gets(ch1);
gets(ch2);
if (x > n || y > n || x < 0 || y < 0) {
puts("Lying");
continue;
}
int k = 0;
for (int i = 0; i < n; i++) {
if (ch1[i] == ch2[i]) {
k++;
}
}
int t1 = min(x, y);
int t2 = max(x, y);
int p = t2 - t1, m = n - k;
if (p <= m && t1 + t2 <= 2 * k + m) {
puts("Not lying");
} else {
puts("Lying");
}
}
}
##Maximum Sequence##
题意:
给一个序列ai,每次可以从b数组里任意选数表示可以从下标为1到n(这里的n,每次制造出来一个新的数n会增长)的数中任意选取一个数下标为j制造出一个数放置在a序列最后面,这个数的值为a j _j j - j,现在问新制造出来的数的和最大是多少。
思路:
拿个优先队列维护一下可用值。
所给的b序列贪心每次取最小的即可。
#include <bits/stdc++.h>
#define MAXN 500050
#define ll long long
#define MOD 1000000007
using namespace std;
struct node {
ll idx, val;
bool operator < (const node &a) const {
return val < a.val;
}
};
priority_queue<node> q;
ll num[MAXN];
void init() {
while (!q.empty()) {
q.pop();
}
}
int main() {
ll n, tmp;
while (~scanf("%lld", &n)) {
init();
for (ll i = 0; i < n; i++) {
scanf("%lld", &tmp);
q.push((node){i + 1, tmp - (i + 1)});
}
for (ll i = 0; i < n; i++) {
scanf("%lld", &num[i]);
}
sort(num, num + n);
ll ans = 0;
for (ll i = 0; i < n; i++) {
while (!q.empty() && q.top().idx < num[i]) {
q.pop();
}
ans += q.top().val;
ans %= MOD;
ll tt = q.top().val;
q.push((node){n + i + 1, tt - (n + i + 1)});
}
printf("%lld\n", ans);
}
}
##Sdjpx Is Happy##
题意:
给一个序列,你可以将这个序列分成任意块,每一个块可以随意排序,你可以将其中两个块换一次位置。
现在问你最多可以分成多少块
思路:
先暴力预处理将每个区间内,可分成块的个数处理出来,若区间最大值和最小值不等于区间长度,则为0,若相对于上一块区间出现了新的最小值,则只为1,因为只能排序不能在这个区间内再分块。
若没制造出新的,则可以等于上一个区间与该区间的块状和。
然后再枚举每一块区间,若满足区间内可拆,则枚举拆点将其拆开(因为有一次交换机会)。有点像dp。。但是就是区间暴力枚举
#include <bits/stdc++.h>
#define MAXN 3010
#define ll long long
using namespace std;
int mn[MAXN][MAXN], mx[MAXN][MAXN];
int sum[MAXN][MAXN];
int a[MAXN], la[MAXN];
int main() {
int T, n;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
memset(sum, 0, sizeof(sum));
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
mn[i][i] = mx[i][i] = a[i];
sum[i][i] = 1;
la[i] = i;
}
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
mn[i][j] = min(mn[i][j - 1], a[j]);
mx[i][j] = max(mx[i][j - 1], a[j]);
}
}
for (int i = 2; i <= n; i++) {
for (int j = 1; j + i - 1 <= n; j++) {
int e = i + j - 1;
if (mx[j][e] - mn[j][e] != e - j) {
sum[j][e] = 0;
} else {
if (mn[j][e] < mn[j][la[j]]) {
sum[j][e] = 1;
} else {
sum[j][e] = sum[j][la[j]] + sum[la[j] + 1][e];
}
la[j] = e;
}
}
}
int ans = sum[1][n];
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
if (sum[i][j] && (i == 1 || (sum[1][i - 1] && mn[1][i - 1] == 1))) {
int p = mx[i][j];
if (p == n || (sum[p + 1][n] && mx[p + 1][n] == n)) {
for (int k = p; k > j; k--) {
if (sum[k][p] && mn[k][p] == i) {
ans = max(ans, sum[1][i - 1] + sum[j + 1][k - 1] + sum[p + 1][n] + 2);
}
}
}
}
}
}
printf("%d\n", ans);
}
return 0;
}
##Funny Function##
题意:有三个算式现在让你求F m , 1 _{m, 1} m,1
思路:
赛后跟机智的队友补题,暴力打表把10x10的数据打了出来。。竟然发现了规律
对于偶数
每行有f(n, m) = f(n - 2, 2) * (2 n ^n n - 1) m − 1 ^{m -1} m−1
每列有f(n, 2) = f(n - 2,2) * 4 + 2
对于奇数
每行有f(n, m) = f(n - 2, 2) * (2 n ^n n - 1) m − 1 ^{m - 1} m−1
每列有f(n, 2) = f(n - 2,2) * 4 + 1
当n或者m为1时,都为1。
然后矩阵快速幂和快速幂求解即可。。
#include <bits/stdc++.h>
#define N 2
#define ll long long
#define MOD 1000000007
using namespace std;
struct mat{
ll mapp[N][N];
};
mat res;
ll rec[] = {2, 5};
void output(mat a) {
printf("=======\n");
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
printf("%lld ", a.mapp[i][j]);
}
puts("");
}
}
ll update(ll a) {
while (a < 0) {
a += MOD;
}
return a;
}
mat mul_mat(mat a, mat b, ll mod, int p) {
mat c;
// output(a);
// puts("-----");
// output(b);
for (int i = 0; i < N; i++) {
for (int j = 0; j < p; j++) {
c.mapp[i][j] = 0;
for (int k = 0; k < N; k++) {
c.mapp[i][j] = update(c.mapp[i][j]);
c.mapp[i][j] += (a.mapp[i][k] * b.mapp[k][j]) % mod;
c.mapp[i][j] %= mod;
}
}
}
return c;
}
mat ksm_mat(mat n, ll k, ll mod, int p) {
mat t = n, ans = res;
while (k) {
if (k & 1) {
ans = mul_mat(t, ans, mod, p);
}
t = mul_mat(t, t, mod, 2);
k >>= 1;
}
return ans;
}
ll ksm(ll n, ll k, ll mod) {
ll temp = n, ans = 1;
while (k) {
if (k & 1) {
ans = update(ans);
ans = (ans * temp) % mod;
}
k >>= 1;
temp = (temp * temp) % mod;
}
return ans % mod;
}
ll get(ll n, ll opt) {
mat p = {4, 1, 0, 1};
if (opt) { //偶数
res = (mat){rec[0], 0, 2, 0};
} else {
res = (mat){rec[1], 0, 1, 0};
}
mat ans = ksm_mat(p, n, MOD, 1);
return ans.mapp[0][0];
}
ll getAns(ll n, ll m) {
if (n == 1 || m == 1) {
return 1;
}
ll ans;
if (n & 1) {
ll a = get(n / 2 - 1, 0);
ll b = get(n / 2 - 1, 1);
res = (mat){a, 0, -b, 0};
mat p = (mat){ksm(2, n, MOD) - 1, 1, 0, 1};
// output(p);
ans = ksm_mat(p, m - 2, MOD, 1).mapp[0][0];
} else {
ans = (get(n / 2 - 1, 1) * ksm(((ksm(2, n, MOD) - 1 + MOD) % MOD), m - 2, MOD)) % MOD;
}
ans = update(ans);
return ans;
}
int main() {
int T;
ll n, m;
scanf("%d", &T);
while (T--) {
scanf("%lld %lld", &n, &m);
printf("%lld\n", getAns(n, m));
}
}
##TrickGCD##
题意:
给一个序列,对于序列的每个数,你可以取1-ai中的任意数,现在让你组成新的序列,问可以组成多少个满足每个区间都满足gcd >= 2。
思路:
用筛法每次枚举每个除数,算区间内的可以被除的数的情况,最后再容斥一下把多余的去掉即可。
#include <iostream>
#include <vector>
#include <map>
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#define MAX_PRIME 200000
#define ll long long
#define MAXN 100005
#define MOD 1000000007
using namespace std;
ll f[MAXN];
ll sum[MAXN];
void init() {
memset(sum, 0, sizeof(sum));
for (ll i = 0; i < MAXN; i++) {
f[i] = 1;
}
}
ll ksm(ll a, ll n) {
ll ans = 1, t = a;
while (n) {
if (n & 1) {
ans = (ans * t) % MOD;
}
t = (t * t) % MOD;
n >>= 1;
}
return ans;
}
int main(){
ll T, n, c, cas = 1;
while (~scanf("%lld", &T)) {
while (T--) {
init();
ll lastans = 0;
scanf("%lld", &n);
for (ll i = 0; i < n; i++) {
scanf("%lld", &c);
sum[c]++;
}
for (ll i = 1; i < MAXN; i++) {
sum[i] += sum[i - 1];
}
for (ll i = 2; i < MAXN; i++) {
if (sum[i - 1]) {
f[i] = 0;
continue;
}
for (ll j = i; j < MAXN; j += i) {
ll k = min(i + j - 1, (ll)(MAXN) - 1);
ll b = sum[k] - sum[j - 1];
ll a = j / i;
f[i] = (f[i] * ksm(a, b)) % MOD;
}
}
for (ll i = MAXN - 1; i >= 2; i--) {
for (ll j = i * 2; j <= MAXN; j += i) {
f[i] = (f[i] - f[j] + MOD) % MOD;
}
}
for (ll i = 2; i < MAXN; i++) {
lastans = (lastans + f[i]) % MOD;
}
printf("Case #%lld: %lld\n", cas++, lastans);
}
}
}
##Regular polygon##
题意:
一个200x200的坐标上有n个点整数点。问能组成多少个不同的正多边形。
题解:
其实题意都是废话。。求正多边形。其实就只有正方形的存在。。
直接判每四个点存不存在就好了。
(orz hdu卡精度搞到写个cos和sin都给卡了。。)
#include <stdio.h>
#include <math.h>
#include <string.h>
#define MAXN 1005
using namespace std;
const double PI = acos(-1.0);
bool mapp[MAXN][MAXN];
struct point {
int x, y;
} p[MAXN];
point xuanzhuan(point a, point b) {
point c;
c.x = -(a.y - b.y) + b.x;
c.y = a.x - b.x + b.y;
return c;
}
int solve(point a, point b) {
for (int i = 0; i < 4; i++) {
point c = xuanzhuan(a, b);
a = b;
b = c;
if (c.x < MAXN && c.x >= 0 && c.y < MAXN && c.y >= 0) {
if (!mapp[c.x][c.y]) {
return 0;
}
} else {
return 0;
}
}
return 1;
}
int main() {
int n, ans;
while (~scanf("%d", &n)) {
ans = 0;
memset(mapp, false, sizeof(mapp));
for (int i = 0; i < n; i++) {
scanf("%d %d", &p[i].x, &p[i].y);
p[i].x += 200;
p[i].y += 200;
mapp[p[i].x][p[i].y] = true;
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i != j) {
ans += solve(p[i], p[j]);
}
}
}
printf("%d\n", ans / 4);
}
}
/*
9
0 0
0 1
1 0
1 1
2 0
0 2
2 2
2 4
4 2
*/