Problem D. Quel’Thalas
题解:
手玩一下就会发现答案是就是2n。
代码:
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#define pi acos(-1)
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 1e6 + 10;
int n;
void solve()
{
cin >> n;
cout << 2 * n << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t;
cin >> t;
while(t--){
solve();
}
}
Problem A. Theramore
题解:
对于奇偶性相同的位置可以任意交换位置,所以对于奇偶位置单独处理即可。
代码:
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#define pi acos(-1)
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 1e6 + 10;
string s;
char a[N], b[N];
void solve()
{
cin >> s;
int len = s.length();
int la = 0, lb = 0;
for(int i = 0; i < len; i++){
if(i % 2) b[++lb] = s[i];
else a[++la] = s[i];
}
sort(a + 1, a + 1 + la);
sort(b + 1, b + 1 + lb);
for(int i = 1; i <= len / 2; i ++) cout << a[i] << b[i];
if(len % 2) cout << b[lb] << '\n';
else cout << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t;
cin >> t;
while(t--){
solve();
}
}
Problem K. Stormwind
题解:
枚举小长方形的某个边长的最小值,由此可以求出另一个边长允许的最小值,然后求出两个方向分别最多能画几条线。
代码:
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#define pi acos(-1)
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 1e6 + 10;
int n, m, k;
void solve()
{
cin >> n >> m >> k;
int ans = -ans;
for(int i = 1; i <= n && i <= k; i ++){
if((k / i + (k % i > 0)) > m) continue;
ans = max(ans, (n / i) + (m / (k / i + (k % i > 0))) - 2);
}
swap(n, m);
for(int i = 1; i <= n && i <= k; i ++){
if((k / i + (k % i > 0)) > m) continue;
ans = max(ans, (n / i) + (m / (k / i + (k % i > 0))) - 2);
}
cout << ans << '\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int t;
cin >> t;
while(t--){
solve();
}
}
Problem E. Ironforge
题解:
求出从每个点出发能到达的最大的范围,即可O(1)回答所有询问。
先从大到小枚举,预处理每个点只向右走的最大范围,如果能向右走一步就先取 i+1 的范围,然后沿着
之前预处理的范围向后跳。向后跳的次数是均摊O(1)的。
然后从小到大枚举,求每个点能走到的最大范围。
如果i能走到i + 1:
如果i + 1已经预处理的向右的范围无法让它走回i则取预处理的答案;
否则i + 1和 i 范围相同。
如果 i 不能走到 i+1:
先取预处理的范围,然后向左向右扩展,都沿着已知的范围暴力跳即可。向左向右跳的次数都是均摊O(1)的。
判断一个范围内是否存在某个质因子,方法有很多,标程采用的是对每个质因子维护一个 vector 存它出现的所有位置,在 vector 上二分。
代码:
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 2e5 + 10;
int n, m, a[N], b[N], clean[N], tim;
int L[N], R[N];
int pr[N], pcnt, vis[N], low[N];
vector<int> pos[N];
bool Check(int p, int l, int r) {
if (!pos[p].size() || pos[p].back() < l) return 0;
int x = *lower_bound(pos[p].begin(), pos[p].end(), l);
return (x <= r);
}
void init() {
n = 200000;
for (int i = 2; i <= n; ++i) {
if (!vis[i]) {
pr[++pcnt] = i;
low[i] = i;
}
for (int j = 1; j <= pcnt && i * pr[j] <= n; ++j) {
vis[i * pr[j]] = 1;
low[i * pr[j]] = pr[j];
if (i % pr[j] == 0) break;
}
}
}
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i < n; ++i) cin >> b[i];
for (int i = 1; i <= n; ++i) {
int x = a[i];
while (x > 1) {
int nowp = low[x];
while (nowp == low[x]) {
x /= nowp;
}
if (clean[nowp] != tim) {
pos[nowp].clear();
clean[nowp] = tim;
}
pos[nowp].push_back(i);
}
}
++tim;
for (int i = n; i >= 1; --i) {
R[i] = i;
while (R[i] < n && Check(b[R[i]], i, R[i])) {
R[i] = R[R[i] + 1];
}
}
for (int i = 1; i <= n; ++i) {
if (i > 1 && R[i - 1] >= i) {
if (Check(b[i - 1], i, R[i])) {
L[i] = L[i - 1];
R[i] = R[i - 1];
} else {
L[i] = i;
}
} else {
L[i] = i;
while (1) {
bool flag = 0;
while (R[i] < n && Check(b[R[i]], L[i], R[i])) {
flag = 1;
R[i] = R[R[i] + 1];
}
while (L[i] > 1 && Check(b[L[i] - 1], L[i], R[i])) {
flag = 1;
L[i] = L[L[i] - 1];
}
if (!flag) break;
}
}
}
for (int i = 1, x, y; i <= m; ++i) {
cin >> x >> y;
if (L[x] <= y && y <= R[x]) cout << "Yes\n";
else cout << "No\n";
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
init();
int t;
cin >> t;
while(t--){
solve();
}
}
Problem G. Darnassus
题解:
依次连接i,i + 1,这样的生成树每条边边权都小于等于n - 1,因此存在一种最小生成树中也
只有边权小于等于n - 1的边(Kruskal 算法的原理)。
代码:
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
//#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
const int N = 5e4 + 5;
int n, pos[N], p[N], rt[N];
int tot, head[N];
struct edg{
int to, fr, next;
}e[N * 460];
int fin(int a){
return (rt[a] == a) ? a : rt[a] = fin(rt[a]);
}
void Insert(int u, int v, int w){
e[++tot].to = v;
e[tot].fr = u;
e[tot].next = head[w];
head[w] = tot;
}
void solve()
{
n = read();
for(int i = 1; i <= n; i++){
p[i] = read();
pos[p[i]] = i;
head[i] = 0;
rt[i] = i;
}
int m = sqrt(n), cnt = n - 1;
long long ans = 0;
tot = 0;
for(int i = 1; i <= n; i++){
for(int j = i + 1; j <= min(n, i + m); j ++){
int tmp = (j - i) * abs(p[i] - p[j]);
if(tmp < n) Insert(i, j, tmp);
tmp = (j - i) * abs(pos[i] - pos[j]);
if(tmp < n) Insert(pos[i], pos[j], tmp);
}
}
for(int i = 1; i < n; i++){
for(int j = head[i]; j; j = e[j].next){
int u = fin(e[j].fr), v = fin(e[j].to);
if(u != v){
rt[u] = v;
ans += i;
cnt --;
}
}
if(cnt == 0) break;
}
printf("%lld\n", ans);
}
signed main(){
// ios::sync_with_stdio(0);
// cin.tie(0); cout.tie(0);
int t;
cin >> t;
while(t--){
solve();
}
}