A - A+B Problem(传送门)
题意
给定一个数组,让你找出满足如下式子的元组个数,其中下标可以升序也可以降序
其中 N<200000
解题思路
FFT ,将负数处理为正数,正对重复的情况进行处理:
- 当选中的数字中有 0 时会造成重复
- 当选中了两个相等的数字的时候会造成重复
将这两种情况分开处理即可
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define FIN freopen("input.txt", "r", stdin)
using namespace std;
typedef long long LL;
const int MAXN = (1 << 18) + 5;
const int SIZE = 5e4 + 5;
const double PI = acos(-1.0);
struct complex {
double r, i;
complex(double _r = 0.0, double _i = 0.0) {
r = _r;
i = _i;
}
complex operator +(const complex &b) {
return complex(r + b.r, i + b.i);
}
complex operator -(const complex &b) {
return complex(r - b.r, i - b.i);
}
complex operator *(const complex &b) {
return complex(r * b.r - i * b.i, r * b.i + i * b.r);
}
};
void change(complex y[], int len) {
int i, j, k;
for(i = 1, j = len / 2; i < len - 1; i++) {
if(i < j)swap(y[i], y[j]);
k = len / 2;
while(j >= k) {
j -= k;
k /= 2;
}
if(j < k) j += k;
}
}
void fft(complex y[], int len, int on) {
change(y, len);
for(int h = 2; h <= len; h <<= 1) {
complex wn(cos(-on * 2 * PI / h), sin(-on * 2 * PI / h));
for(int j = 0; j < len; j += h) {
complex w(1, 0);
for(int k = j; k < j + h / 2; k++) {
complex u = y[k];
complex t = w * y[k + h / 2];
y[k] = u + t;
y[k + h / 2] = u - t;
w = w * wn;
}
}
}
if(on == -1)
for(int i = 0; i < len; i++)
y[i].r /= len;
}
int n;
LL B[MAXN];
LL A[MAXN];
complex x[MAXN];
int main() {
//FIN;
int t;
while(~scanf("%d", &n)) {
int len = 1 << 18;//必须为2的指数幂
memset(A, 0, sizeof(A));
int cnt = 0;
for(int i = 0; i < n; i ++) {
scanf("%lld", &B[i]);
A[SIZE + B[i]] ++;
if(B[i] == 0)cnt ++;
}
for(int i = 0; i < len; i ++) {
x[i] = complex(A[i], 0);
}
fft(x, len, 1);
for(int i = 0; i < len; i ++) {
x[i] = x[i] * x[i];
}
fft(x, len, -1);
LL ans = 0;
for(int i = 0; i < len; i ++) {
A[i] = (LL)(x[i].r + 0.5);
}
for(int i = 0; i < n; i ++) {
A[B[i] * 2 + SIZE * 2] --;
}
for(int i = 0; i < n; i ++) {
if(B[i] != 0) {
ans += A[B[i] + SIZE * 2];
ans -= cnt * 2;
} else {
ans += A[B[i] + SIZE * 2];
ans -= (cnt - 1) * 2;
}
}
printf("%lld\n", ans);
}
return 0;
}
B - Boxes(传送门)
题意
给定几个方块和他们的包含与被包含的关系,找出给定的几个方块中包含了多少个方块
解题思路
直接暴力,判断两个方块是否有包含关系,如果有,去掉那个被包含的方块,接着统计数目
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 2e5 + 5;
int n;
int par[MAXN];
vector<int>B[MAXN];
void init(int n){
for(int i = 0;i <= n;i ++){
par[i] = i;
}
for(int i = 0;i <= n;i ++) B[i].clear();
}
void B_merge(int u, int v){
par[u] = v;
}
bool _find(int u, int v){
if(u == v) return true;
if(par[v] == v) return false;
return _find(u, par[v]);
}
int sum(int u){
int ret = 1;
for(int i = 0;i < B[u].size();i ++){
ret += sum(B[u][i]);
}
return ret;
}
int K[MAXN];
int main() {
while(~scanf("%d", &n)){
init(n);
int x;
for(int i = 1;i <= n;i ++){
scanf("%d", &x);
if(x == 0) continue;
B_merge(i, x);
B[x].push_back(i);
}
int q, m;
scanf("%d", &q);
while(q --){
scanf("%d", &m);
for(int i = 0;i < m;i ++){
scanf("%d", &K[i]);
}
for(int i = 0;i < m;i ++){
for(int j = i + 1;j < m;j ++){//去掉被包含
if(_find(K[i], K[j])){
K[j] = -1;
}
else if(_find(K[j], K[i])){
K[i] = -1;
}
}
}
int ans = 0;
for(int i = 0;i < m;i ++){
if(K[i] == -1) continue;
ans += sum(K[i]);
}
printf("%d\n", ans);
}
}
return 0;
}
C - Classrooms(传送门)
题意
给定
解题思路
针对每一个班级,处理结束时间靠前并且开始时间离自己近的活动,这是为了让其他的班级更好的拥有更多的时间去进行其它的活动,用 multimap 来处理数据就可以了
代码
#include <cstdio>
#include <cstring>
#include <queue>
#include <map>
#include <algorithm>
#define FIN freopen("input.txt", "r", stdin)
using namespace std;
typedef pair<int, int> PII;
const int MAXN = 4e5 + 5;
int n, k;
struct o {
int l, r, sz;
bool operator < (const o &p) const {
return r < p.r;
}
} O[MAXN];
multimap<int,int>MT;
int main() {
//FIN;
while(~scanf("%d%d", &n, &k)) {
MT.clear();
for(int i = 0; i < n; i ++) {
scanf("%d%d", &O[i].l, &O[i].r);
}
sort(O, O + n);
for(int i = 0;i < k;i ++){
MT.insert(make_pair(0, 0));//初始化每一个班级的状态
}
multimap<int,int>::iterator it;
for(int i = 0;i < n;i ++){
o &p = O[i];
it = MT.lower_bound(p.l);//找离自己最近的
if(it == MT.begin()) continue;
it --;
PII np = PII(p.r, it -> second + 1);
MT.erase(it);
MT.insert(np);
}
int sum = 0;
for(it = MT.begin();it != MT.end();it ++){
sum += it -> second;
}
printf("%d\n", sum);
}
return 0;
}
D - Curious Cupid(传送门)
题意
给定有
n
个男人和
解题思路
莫队算法处理离散区间问题时间复杂度为
O(N(N))
,他的操作仅仅只是改变了操作数据的顺序,先将
L
限定在一个区间,然后按照
代码
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define FIN freopen("input.txt", "r", stdin)
using namespace std;
const int MAXN = 5e4 + 5;
const int MAXM = 1e6 + 5;
struct o {
static int unit;
int l, r, idx;
bool operator < (const o &p) const {
if(l / unit != p.l / unit) return l / unit < p.l / unit;
return r < p.r;
}
} O[MAXN];
int o::unit = 1;
int A[MAXN], B[MAXN];
int ans[MAXN];
int numA[MAXM], numB[MAXM];
int sum;
void add(int pos) {
if(A[pos] == B[pos]) {
sum += 1;
numA[A[pos]] ++, numB[B[pos]] ++;
return;
}
sum -= min(numA[A[pos]], numB[A[pos]]);
sum -= min(numB[B[pos]], numA[B[pos]]);
numA[A[pos]] ++;
numB[B[pos]] ++;
sum += min(numA[A[pos]], numB[A[pos]]);
sum += min(numB[B[pos]], numA[B[pos]]);
}
void remove(int pos) {
if(A[pos] == B[pos]) {
sum -= 1;
numA[A[pos]] --, numB[B[pos]] --;
return;
}
sum -= min(numA[A[pos]], numB[A[pos]]);
sum -= min(numB[B[pos]], numA[B[pos]]);
numA[A[pos]] --;
numB[B[pos]] --;
sum += min(numA[A[pos]], numB[A[pos]]);
sum += min(numB[B[pos]], numA[B[pos]]);
}
int N, M, K;
int main() {
//FIN;
while(~scanf("%d%d%d", &N, &M, &K)) {
memset(numA, 0, sizeof(numA));
memset(numB, 0, sizeof(numB));
for(int i = 1; i <= N; i ++) {
scanf("%d", &A[i]);
}
for(int i = 1; i <= N; i ++) {
scanf("%d", &B[i]);
}
for(int i = 0; i < M; i ++) {
scanf("%d%d", &O[i].l, &O[i].r);
O[i].l ++;
O[i].r ++;
O[i].idx = i;
}
o::unit = (int)sqrt(N);
sort(O, O + M);
int L = 1, R = 0;
sum = 0;
//[]L,R代表左闭右闭区间
for(int i = 0; i < M; i ++) {
while(L < O[i].l) {
remove(L ++);
}
while(L > O[i].l) {
add(-- L);
}
while(R < O[i].r) {
add(++ R);
}
while(R > O[i].r) {
remove(R --);
}
ans[O[i].idx] = sum;
}
for(int i = 0; i < M; i ++) {
printf("%d\n", ans[i]);
}
}
return 0;
}
F - Crazy Driver(传送门)
题意
在一条线上有若干个门,每一个门之间有一条路,从一个门开车到另外一个门的时间为
1
小时,但是花费为
解题思路
方法一就是单纯的模拟,个人提供的代码就是这个,模拟真实开车者取得最有状态时的开车路径即可,针对当前这条路,不断检测接着往下走是否会更优
方法二就是贪心,针对沿途遇到的最小边进行来回走动即可,认真处理这个事件 1A 没问题的
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#define FIN freopen("input.txt", "r", stdin)
#define FOUT freopen("output.txt", "w", stdout);
typedef long long LL;
using namespace std;
const int MAXN = 1e5 + 5;
const int MAXM = 1e6 + 5;
const LL INF = 1e14 + 5;
int n;
LL T[MAXN];
LL C[MAXN];
LL dp[MAXN], mindp[MAXN], _rank[MAXN], cdp[MAXN], sum[MAXN];
/*
dp[i]表示到达i这个门时的最小时间花费
mindp[i]表示到i这个门最小花费的边是哪一条
_rank[i]表示到i这个门最小花费的边的前一个门是哪一个
cdp[i]表示到i这个门总花费最小为多少
sum[i]表示从1门到i门的花费前缀和
*/
int main() {
//FIN;
//FOUT;
while(~scanf("%d", &n)) {
for(int i = 1; i < n; i ++) {
scanf("%lld", &C[i]);
}
for(int i = 1; i <= n; i ++) {
scanf("%lld", &T[i]);
}
sum[1] = 0;
for(int i = 2;i <= n;i ++){
sum[i] = sum[i - 1] + C[i - 1];
}
for(int i = 0;i <= n;i ++){
dp[i] = 0;
mindp[i] = INF;
cdp[i] = INF;
_rank[i] = i;
}
for(int i = 2;i <= n;i ++){
T[i] = max(T[i], T[i - 1] + 1);
if(mindp[i] > C[i - 1]){
mindp[i] = C[i - 1];
_rank[i] = i - 1;
}
if(mindp[i] > mindp[i - 1]){
mindp[i] = mindp[i - 1];
_rank[i] = _rank[i - 1];
}
}
dp[1] = cdp[1] = 0;
for(int i = 2;i <= n;i ++){
LL ct = T[i] - dp[_rank[i]];
if(ct <= i - _rank[i]){
cdp[i] = cdp[_rank[i]] + sum[i] - sum[_rank[i]];
dp[i] = dp[_rank[i]] + i - _rank[i];
}
else{
ct -= i - _rank[i];
if(ct & 1) ct ++;
cdp[i] = cdp[_rank[i]] + sum[i] - sum[_rank[i]] + mindp[i] * ct;
dp[i] = dp[_rank[i]] + ct + i - _rank[i];
}
}
printf("%lld\n", cdp[n]);
}
return 0;
}
J - 0-1 Sequences(传送门)
题意
给定一个由 ′0′,′1′,′?′ 三种字符组成的字符串,求让这个字符串变为不降序的操作和为多少
解题思路
从后面往前面进行遍历不断的找寻零的个数,当遇到
1
,这些后面的
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
const int MAXN = 5e5 + 5;
char S[MAXN];
int main() {
while(~scanf("%s", S)) {
int len = strlen(S);
LL ans = 0, s0 = 0, sc = 1;
for(int i = len - 1;i >= 0;i --){
if(S[i] == '?'){
ans = (ans * 2LL + s0) % mod;
s0 = (s0 * 2LL + sc) % mod;
sc = sc * 2LL % mod;
}
else if(S[i] == '1'){
ans = (ans + s0) % mod;
}
else{
s0 = (s0 + sc) % mod;
}
}
printf("%I64d\n", ans);
}
return 0;
}
K - Take Two Stones(传送门)
题意
搬石头,意思自己懂的
解题思路
不想说了,看代码就知道多简单了,尴尬
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 2e5 + 5;
int n;
int main() {
while(~scanf("%d", &n)){
if(n & 1) printf("Alice\n");
else printf("Bob\n");
}
return 0;
}