AtCoder Regular Contest 118 VP 记录
赛时 1h 才过一题,最后过了三题倒还好。
A - Tax Included Price
给定 \(t,n\),让所有正整数 \(i\) 变为 \(\left\lfloor\frac{100+t}{100} \times i \right \rfloor\) ,问第 \(n\) 个没出现的正整数是什么。
考虑什么时候会多 \(1\)。
因为 \(\frac{100+t}{100} \times i = i + \frac{ti}{100}\) ,所以当 \(ti\ge100\) 的时候会有第一个正整数没出现,所以第 \(n\) 个就是:
设 \(p\) 是满足 \(tp \ge 100n\) 的最小的 \(p\),那么答案就是 \(p + \frac{tp}{100}-1\)。
signed main() {
// Check();
int t = read(), n = read();
int p = 100 * n / t + (100 * n % t > 0);
printf("%lld\n", p + t * p / 100 - 1);
return 0;
}
B - Village of M People
给你一个长度为 \(K\) 的序列 \(a\),且 \(\sum_i a_i = n\) ,你需要填一个序列 \(b\),需要满足 \(\sum_i b_i = m\),要使得 \(\max_i |\frac{b_i}{m} - \frac{a_i}{n}|\) 的最小。
直接二分,然后算贡献即可。
需要注意的是,这个东西要先让 \(|\frac{b_i}{m} - \frac{a_i}{n}|\ge - mid\),如果连着都不满足也是不合法的。
然后看看能不能把剩下的 \(x\) 分配到 \(b\) 里使得 \(|\frac{b_i}{m} - \frac{a_i}{n}| \le mid\)。
虽然看上去比较麻烦,但它也有可二分性。
#include<bits/stdc++.h>
#define LL long long
#define int long long
#define orz cout << "tyy YYDS!!!\n"
using namespace std;
const int MAXN = 2e5 + 10;
const int INF = 1e18 + 7;
const int mod = 998244353;
int read() {
int s = 0, f = 0; char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
int K, n, m;
int a[MAXN];
int cnt[MAXN];
int b[MAXN];
bool Check(int lim) {
int res = 0;
for(int i = 1; i <= K; ++i) b[i] = - m * a[i], cnt[i] = 0;
for(int i = 1; i <= K; ++i) {
if(b[i] < - lim) {
int tmp = (- lim - b[i] - 1) / n + 1;
b[i] += tmp * n;
res += tmp, cnt[i] += tmp;
}
}
if(res > m) return false;
for(int i = 1; i <= K; ++i) {
if(b[i] < lim) {
int tmp = min(m - res, (int)(lim - b[i]) / n);
res += tmp, cnt[i] += tmp, b[i] += n * tmp;
}
}
return res >= m;
}
signed main() {
K = read(), n = read(), m = read();
for(int i = 1; i <= K; ++i) a[i] = read();
int l = INF, r = - INF;
for(int i = 1; i <= K; ++i) {
l = min(l, - m * a[i]);
r = max(r, n * m - m * a[i]);
}
r = max(-l, r), l = 0;
int ans = l;
while(l <= r) {
int mid = (l + r) / 2;
if(Check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
Check(ans);
for(int i = 1; i <= K; ++i) printf("%lld ", cnt[i]);
return 0;
}
C - Coprime Set
构造一个长度为 \(n\) 的序列 \(a\),满足下面条件:
- \(1 \le a_i \le 10000\);
- 任意两个位置不同,任意两个位置的 \(\gcd > 1\)。
- 整个序列的 \(\gcd =1\)。
\(3 \le n \le 2500\)。
考虑 \(n=3\) 的时候,肯定是选三个质因子,然后一个数拿两个。
考虑 \(n >3\) 的时候,肯定也是拿两个质因子,然后再带一个其他的倍数,其他的倍数直接枚举即可。
那么三个质因子我们选 \(2,3,5\),那么剩下的数都让他们是 \(2 \times 3\) 或者 \(3 \times 5\) 或者 \(2 \times 5\) 倍数就行。
发现在 \(15 < x \le 2500\) 的数中,这样的数有 \(2662\) 个,够用了。
#include<bits/stdc++.h>
#define LL long long
//#define int long long
#define orz cout << "tyy YYDS!!!\n"
using namespace std;
const int MAXN = 2e5 + 10;
const int INF = 1e9 + 7;
const int mod = 998244353;
int read() {
int s = 0, f = 0; char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
int n;
int a[MAXN], stc[MAXN], sc = 0;
int prim[MAXN], Cnt = 0;
bool vis[MAXN];
void Init(int M) {
for(int i = 2; i <= M; ++i) {
if(!vis[i]) prim[++Cnt] = i;
for(int j = 1; j <= Cnt && i * prim[j] <= M; ++j) {
vis[i * prim[j]] = true;
if(i % prim[j] == 0) break;
}
}
}
signed main() {
Init(3333);
int p = 0;
for(int i = 16; i <= 10000; ++i)
if(i % 6 == 0 || i % 10 == 0 || i % 15 == 0)
stc[++sc] = i;
n = read();
a[1] = 2 * 3, a[2] = 3 * 5, a[3] = 2 * 5;
for(int i = 4; i <= n; ++i) a[i] = stc[sc--];
for(int i = 1; i <= n; ++i) cout << a[i] << " "; puts("");
return 0;
}
D - Hamiltonian Cycle
给一个 \(P\),构造一个长度为 \(P\) 的序列 \(a\),\(a_1 = a_P = 1\) ,\(a[1\sim P-1]\) 是 \(P-1\) 的排列。
并且相邻两个数满足下面的四个条件之一:
- \(a_i \equiv x a_{i-1} \pmod P\)
- $ a_{i-1} \equiv x a_{i} \pmod P$
- \(a_i \equiv y a_{i-1} \pmod P\)
- \(a_{i-1} \equiv y a_{i} \pmod P\)
\(P,x,y\) 给定。
\(2 \le P \le 10^5\)。
根据题目来看怎么都像是哈密尔顿回路好吧。
但是你根据题目的规则建一个图出来也不能在一个多项式复杂度内找到一个长度为 \(n\) 的环。
所以应该找一些特殊性质。
我们考虑构建一个平面图
设 \(n\) 是最小的 \(a^n \equiv 1 \pmod P\)。
把 \(a^i, 0 \le i < n\) 加入一个集合 \(S\),设 \(m\) 是最小的 \(b^i \bmod P \in S\)。
那么我们能构建出一个 \(n\) 行 \(m\) 列的平面图。
第 \(i\) 行第 \(j\) 列表示的是 \(a^i b^j\),不难发现,从一个位置向四周哪里走都是可以的。
如果 \(n \times m = P-1\) 则有解,否则交换 \(x,y\) 再这样判断一遍,如果依旧不行那么就无解。
如果有解的话
问题就转化成了从左上角开始走一个哈密尔顿回路。
针对 \(n,m\) 的奇偶性分别构造一个对应的路径即可。
#include<bits/stdc++.h>
#define LL long long
#define int long long
#define orz cout << "tyy YYDS!!!\n"
using namespace std;
const int MAXN = 2e5 + 10;
const int INF = 1e9 + 7;
const int mod = 998244353;
int n, a, b;
int N, M;
vector<int> c[MAXN];
bool vis[MAXN];
int read() {
int s = 0, f = 0; char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
bool Check(int a, int b) {
memset(vis, false, sizeof vis);
for(int i = 1, x = 1; i < n; ++i) {
vis[x] = true;
x = x * a % n;
if(x == 1) { N = i; break; }
}
for(int i = 1, x = 1; i < n; ++i) {
x = x * b % n;
if(vis[x]) { M = i; break; }
}
return N * M == n - 1;
}
signed main() {
n = read(), a = read(), b = read();
if(n == 2) return puts("Yes"), puts("1 1"), 0;
if(!Check(a, b)) return puts("No"), 0;
swap(a, b);
if(!Check(a, b)) return puts("No"), 0;
puts("Yes");
for(int i = 0, x = 1; i < N; ++i, x = x * a % n)
for(int j = 0, p = x; j < M; ++j, p = p * b % n)
c[i].push_back(p);
if(N == 1) {
for(auto v : c[0]) printf("%lld ", v);
puts("1");
} else if(M == 1) {
for(int i = 0; i < N; ++i) printf("%lld ", c[i][0]);
puts("1");
} else if(M % 2 == 0 || N % 2 == 1) {
for(int i = 0; i < M; ++i) printf("%lld ", c[0][i]); puts("");
for(int i = M - 1, v = 1; i >= 0; --i, v ^= 1) {
if(v == 1) {
for(int j = 1; j < N; ++j) printf("%lld ", c[j][i]);
} else {
for(int j = N - 1; j >= 1; --j) printf("%lld ", c[j][i]);
}
}
puts("1");
} else if(M % 2 == 1) {
for(int i = 0, v = 1; i < N; ++i, v ^= 1) {
if(v) {
for(int j = 0; j < M; ++j) cout << c[i][j] << " ";
} else {
for(int j = M - 1; j >= 0; --j) cout << c[i][j] << " ";
}
}
puts("1");
}
return 0;
}