【AtCoder】Tenka1 Programmer Contest(C

C - Align

考的时候,我大胆猜了结论,就是一小一大一小一大这么排
证明的话,由于我们总是要加上相邻的最大值而减去最小值,我们就让最大值都保持在前面

如果长度为奇数,要么就是大小大小大,要么是小大小大小
第一种要求是靠中间的位置填(n + 1) / 2个最大值中较大的,两边填较小的(两边只被加了一次)
第二种要求是靠中间的位置填(n + 1) / 2个最小值中较小的,两边填较大的(两边被减了一次)

如果长度为偶数
小大小大小大和大小大小大小显然等价

我们把最小值中较大的放在最前,最大值较小的放在最后即可

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define space putchar(' ')
#define enter putchar('
')
//#define ivorysi
#define MAXN 100005
using namespace std;
typedef long long int64;
template<class T>
void read(T &res) {
	res = 0;char c = getchar();T f = 1;
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') {
		res = res * 10 + c - '0';
		c = getchar();
	}
	res *= f;
}
template<class T>
void out(T x) {
	if(x < 0) {x = -x;putchar('-');}
	if(x >= 10) out(x / 10);
	putchar('0' + x % 10);
}
int N;
int A[MAXN];
int main() {
#ifdef ivorysi
	freopen("f1.in","r",stdin);
#endif
	read(N);
	for(int i = 1 ; i <= N ; ++i) read(A[i]);
	sort(A + 1,A + N + 1);
	if(N & 1) {
		int t = N / 2 + 1;
		int L = 1,R = N;int64 res = 0;
		for(int i = 1 ; i <= t - 2; ++i) {
			res -= 2 * A[i];
		}
		res = res - A[t - 1] - A[t];
		for(int i = t + 1 ; i <= N ; ++i) res += 2 * A[i];
		int64 tmp = 0;
		t = N - (N / 2 + 1) + 1;
		for(int i = N ; i >= t + 2 ; --i) tmp += 2 * A[i];
		tmp += A[t + 1] + A[t];
		for(int i = 1 ; i < t ; ++i) tmp -= 2 * A[i];
		res = max(res,tmp);
		out(res);enter;
	}
	else {
		int L = 1,R = N;
		int64 res = 0;
		for(int i = 1 ; i <= N - 2 ; ++i) {
			if(i & 1) res -= 2 * A[L++];
			else res += 2 * A[R--];
		}
		res += A[R] - A[L];
		out(res);enter;
	}
	return 0;
}

D - Crossing

把每个元组之间有两个相同元素当做唯一的连边方式,那么k个元组需要的边是
(frac{k(k - 1)}{2}),我们先看N能不能解出这个k,然后构造方法就是暴力给没连边的元组连边就好了

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define space putchar(' ')
#define enter putchar('
')
//#define ivorysi
#define MAXN 100005
using namespace std;
typedef long long int64;
template<class T>
void read(T &res) {
	res = 0;char c = getchar();T f = 1;
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') {
		res = res * 10 + c - '0';
		c = getchar();
	}
	res *= f;
}
template<class T>
void out(T x) {
	if(x < 0) {x = -x;putchar('-');}
	if(x >= 10) out(x / 10);
	putchar('0' + x % 10);
}
int N;
int a[1005][1005];
int main() {
#ifdef ivorysi
	freopen("f1.in","r",stdin);
#endif
	read(N);
	int t = 0,k = 0;
	for(int i = 1 ; i <= N ; ++i) {
		t += i;
		if(t >= N) {k = i;break;}
	}
	if(t != N) {puts("No");}
	else {
		puts("Yes");
		out(k + 1);enter;
		int cnt = 0;
		for(int j = 1 ; j <= k ; ++j) {
			for(int i = j ; i <= k ; ++i) {
				a[j][i] = ++cnt;
			}
			for(int i = j + 1 ; i <= k + 1 ; ++i) {
				a[i][j] = a[j][i - 1];
			}
		}
		for(int i = 1 ; i <= k + 1 ; ++i) {
			out(k);
			for(int j = 1 ; j <= k ; ++j) {
				space;out(a[i][j]);
			}
			enter;
		}
	}
	return 0;
}

E - Equilateral

先熟练地转一下切比雪夫距离

容易发现这个三元组必然有两个在同一水平线或者同一竖直线上,合法的点一定是在某两个和坐标轴平行的条内,然后记录一下转完坐标系后的矩阵和,两两枚举所有水平线和竖直线上的所有点,然后计算合法的点

防止重复统计可以要求同一水平线上的点的三元组第三个和任两个不在同一竖直线,或者反过来

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define space putchar(' ')
#define enter putchar('
')
//#define ivorysi
#define MAXN 100005
using namespace std;
typedef long long int64;
template<class T>
void read(T &res) {
	res = 0;char c = getchar();T f = 1;
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') {
		res = res * 10 + c - '0';
		c = getchar();
	}
	res *= f;
}
template<class T>
void out(T x) {
	if(x < 0) {x = -x;putchar('-');}
	if(x >= 10) out(x / 10);
	putchar('0' + x % 10);
}
int H,W;
char s[305][305];
int sum[605][605];
vector<int> row[605],col[605];
int Query1(int x1,int x2,int y) {
	if(x2 < x1) return 0;
	if(y < 1 || y > H + W) return 0;
	x2 = min(x2,H + W);
	x1 = max(x1,1);
	return sum[x2][y] - sum[x1 - 1][y] - sum[x2][y - 1] + sum[x1 - 1][y - 1];
}
int Query2(int x,int y1,int y2) {
	if(y2 < y1) return 0;
	if(x < 1 || x > H + W) return 0;
	y2 = min(y2,H + W);
	y1 = max(y1,1);
	return sum[x][y2] - sum[x][y1 - 1] - sum[x - 1][y2] + sum[x - 1][y1 - 1];
}
int main() {
#ifdef ivorysi
	freopen("f1.in","r",stdin);
#endif
	read(H);read(W);
	for(int i = 1 ; i <= H ; ++i) {
		scanf("%s",s[i] + 1);
	}
	for(int i = 1 ; i <= H ; ++i) {
		for(int j = 1 ; j <= W ; ++j) {
			if(s[i][j] == '#') {
				sum[i + j][i - j + W]++;
				row[i + j].pb(i - j + W);
				col[i - j + W].pb(i + j);
			}
		}
	}
	for(int i = 1 ; i <= H + W ; ++i) {
		for(int j = 1 ; j <= H + W ; ++j) {
			sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
		}
	}
	int64 ans = 0;
	for(int i = 1 ; i <= H + W ; ++i) {
		int s = row[i].size();
		sort(row[i].begin(),row[i].end());
		for(int j = 0 ; j < s ; ++j) {
			for(int k = j + 1 ; k < s ; ++k) {
				int l = row[i][k] - row[i][j];
				ans += Query2(i - l,row[i][j],row[i][k]);
				ans += Query2(i + l,row[i][j],row[i][k]);
			}
		}
	}
	for(int i = 1 ; i <= H + W ; ++i) {
		int s = col[i].size();
		sort(col[i].begin(),col[i].end());
		for(int j = 0 ; j < s ; ++j) {
			for(int k = j + 1 ; k < s ; ++k) {
				int l = col[i][k] - col[i][j];
				ans += Query1(col[i][j] + 1,col[i][k] - 1,i - l);
				ans += Query1(col[i][j] + 1,col[i][k] - 1,i + l);
			}
		}
	}
	out(ans);enter;
	return 0;
}

F - Circular

比赛的时候没时间想了,后来把计数方案推完,我觉得这个方案真的非常假……(但我一时间没想到反例……)
居然A了?

这题的英文题解貌似咕着,感觉能赚一点访问量了

显然如果都是1的话方案是(n!),这个特判掉

其次这个序列必然是相同的数都在连续的一段,不能有穿插的数,例如样例3 1 4 1 5,很好想,不说了

然后我们找到1所在的段长,这肯定是序列进行的操作次数

为了方便,我们断环为链,把1扯出来作为第一个数

然后剩下的段长都不能超过这个长度,否则输出0

设段长为(len),当前位置的数(i)肯定是比([i,i + len - 1])的最大值要大,这个你单调队列还是写rmq随意吧,都求出来,记为(val[i])

然后对于连续一段的数([l,r])这个数位(num),这个限制了(num)在某个区间里
这个区间就是([r + 1 - len,l])我们计算一下这个区间里的数有几个(val[i])(num)相同,这个时候,假如这个值是0,那么无解

统计的时候我们记录一个(cnt[x])表示(val[i] == x)的个数,处理成前缀和
统计的时候枚举每一个数,假如这个数有必选位置,我们乘上必选位置的个数,否则乘上(cnt[i] - (i - 1))也就是(cnt[i])(i)能在的位置,这些位置有且仅有(i - 1)个被占用了

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define space putchar(' ')
#define enter putchar('
')
#define mp make_pair
#define pb push_back
//#define ivorysi
#define MAXN 300005
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
    }
    while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {x = -x;putchar('-');}
    if(x >= 10) {
    	out(x / 10);
    }
    putchar('0' + x % 10);
}
const int MOD = 998244353;
int N,a[MAXN * 2],A[MAXN],st[MAXN][20],val[MAXN],len[MAXN],must[MAXN],sum[MAXN],ql,qr,cnt[MAXN];
bool vis[MAXN];
int inc(int a,int b) {
    return a + b >= MOD ? a + b - MOD : a + b;
}
int mul(int a,int b) {
    return 1LL * a * b % MOD;
}
int Query(int l,int r) {
    int t = len[r - l + 1];
    return max(st[l][t],st[r - (1 << t) + 1][t]);
}
void Move(int l,int r) {
    while(qr < r) sum[val[++qr]]++;
    while(ql < l) sum[val[ql++]]--;
}
void Solve() {
    read(N);bool all_1 = 1;
    for(int i = 1 ; i <= N ; ++i) {read(a[i]);a[i + N] = a[i];if(a[i] != 1) all_1 = 0;}
    if(all_1) {
        int res = 1;
        for(int i = 1 ; i <= N ; ++i) res = mul(res,i);
        out(res);enter;return;
    }
    int s = 0;
    for(int i = 2 ; i <= 2 * N ; ++i) {
        if(a[i] == 1 && a[i - 1] != 1) {
            s = i;
            break;
        }
    }
    if(!s) {puts("0");return;}
    for(int i = 1 ; i <= N ; ++i) A[i] = a[s++];
    int p = 1;
    while(A[p] == 1) ++p;
    --p;
    int l = 0;
    for(int i = 1 ; i <= N ; ++i) {
        if(A[i] != A[i - 1]) {
            if(vis[A[i]]) {puts("0");return;}
            vis[A[i]] = 1;l = 0;
        }
        ++l;
        if(l > p) {puts("0");return;}
    }
    for(int i = 1 ; i <= N ; ++i) st[i][0] = A[i];
    for(int j = 1 ; j <= 19 ; ++j) {
        for(int i = 1 ; i <= N ; ++i) {
            if(i + (1 << j) - 1 > N) break;
            st[i][j] = max(st[i][j - 1],st[i + (1 << j - 1)][j - 1]);
        }
    }
    for(int i = 2 ; i <= N ; ++i) len[i] = len[i / 2] + 1;
    for(int i = 1 ; i <= N ; ++i) {
        int r = min(i + p - 1,N);
        val[i] = Query(i,r);
    }
    int t = 0;ql = 1,qr = 0;
    for(int i = 1 ; i <= N ; ++i) {
        if(A[i] != A[i - 1]) {
            if(t) {
                s = max(1,i - p);
                Move(s,t);
                must[A[i - 1]] = sum[A[i - 1]];
                if(!sum[A[i - 1]]) {puts("0");return;}
            }
            t = i;
        }
    }
    s = max(1,N - p + 1);
    Move(s,t);
    must[A[N]] = sum[A[N]];
    if(!sum[A[N]]) {puts("0");return;}
    for(int i = 1 ; i <= N ; ++i) cnt[val[i]]++;
    for(int i = 1 ; i <= N ; ++i) cnt[i] += cnt[i - 1];
    int ans = 1;
    for(int i = 1 ; i <= N ; ++i) {
        if(must[i]) {ans = mul(ans,must[i]);}
        else ans = mul(ans,cnt[i] - (i - 1));
    }
    out(ans);enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
    return 0;
}
原文地址:https://www.cnblogs.com/ivorysi/p/9875670.html