牛子题集锦
CF468C Hack it!
我们设 \(\sum_{i = 1} ^ {10^{18}} f(i) \equiv P \pmod a\),则 \(\sum_{i = 2}^{10^{18} + 1} \equiv P + 1 \pmod a\),依次类推下去。由于 \(a \le 10^{18}\),因此一定可以找到一个 \(\sum_{i = s}^{10^{18} + s} \equiv 0 \pmod a\),现在我们要求出 \(s\) 的值,那么显然 \(s = a - P\),重点就在于求 \(P\) 的值。容易知道:
这样递归下去跑一下得出答案:\(81 \times 10^{18}\)。最后注意取模溢出即可。
ARC147E Examination
考虑一个选择集合 \(S\),这些集合中的数可以随意交换,并满足所有的 \(a_i < b_i\) 的 \(i\) 都在其中。那么这个集合合法的条件即为对于所有 \(a_i\),\(b_i\) 分别从小到大排序后满足 \(\forall i \in S, a_i > b_i\)。转化成差的形式就是:
\(\forall V, \sum_{i \in S} [b_i \le V] - [a_i \le V]\ge 0\)。
那么我们先将 \(a_i < b_i\) 的 \(i\) 加入 \(S\) 中,考虑找到最小的 \(V\),使得不满足上述条件,则我们通过加入一些 \(a_i > b_i\) 的点进行调整。显然对于 \(V\) 有贡献的点 \(j\) 一定满足 \(b_j \le V < a_j\),那么我们维护一个满足 \(b_j\) 从小到大的堆,并每次将 \(b_j \le V\) 的加入到一个满足 \(a_j\) 从大到小的堆即可。
qwq
#include<bits/stdc++.h>
#define int long long
#define pir pair<int, int>
using namespace std;
const int N = 3e5 + 10;
map<int, int> tot;
priority_queue<pir, vector<pir>, greater<pir> > QB;
priority_queue<int, vector<int>, less<int> >QA;
int n;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n; int ans = n, now = 0;
for(int i = 1; i <= n; i++){
int a, b; cin >> a >> b;
if(a < b){tot[b]++; tot[a]--; ans--;}
else QB.push(make_pair(b, a));
}
for(map<int, int>::iterator it = tot.begin(); it != tot.end(); it++){
pir ths = (*it); now += ths.second;
while((!QB.empty()) && QB.top().first <= ths.first){
QA.push(QB.top().second); QB.pop();
}
while(now < 0){
if(QA.empty()){cout << -1; return 0;}
if(QA.top() > ths.first){
tot[QA.top()]--; ans--; now++;
}
QA.pop();
}
}
cout << ans;
return 0;
}
AGC027D Modulo Matrix
神秘人类智慧题。
考虑对原棋盘进行黑白染色,对于任意相同颜色的格子不会相互影响。考虑一个被 \(4\) 个黑格子夹住的白格子,设模数为 \(M\),显然一种合法的构造方就是让它等于周围四个值的 \(\operatorname{LCM} + M\)。由于有一个限制 \(a_{i, j} \le 10^{15}\),我们考虑让 \(a\) 尽量小。于是我们令 \(M = 1\)。于是我们只考虑黑色格子。注意到我们还有一个 \(a_{i, j}\) 互不相等的限制,于是我们考虑让黑色格子的数等于质数。这样黑色格子中的点互不相等,白色格子由于奇偶性的不同也不会相等。
但是这样数最大的大小约是 \(O(V^8), V = N \ln N\),显然超出限制。由于我们只关心 \(\max{a}\),而且白的很大,黑的很小,于是我们可以考虑平衡一下,让黑色格子等于两个质数的乘积,并且每条对角线的质因子一样,这样调整一下便可以通过限制。
qwq
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e3 + 10;
int n, a[N][N], b[N][N], c[N][N], prime[N * N], tot, cnt;
bool isprime[N * N];
void init(){
for(int i = 2; i < N * N; i++){
if(!isprime[i]) prime[++tot] = i;
for(int j = 1; j <= tot && i * prime[j] < N * N; j++){
isprime[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
}
int LCM(int v1, int v2, int v3, int v4){
int val = 1;
if(v1) val *= v1 / __gcd(val, v1);
if(v2) val *= v2 / __gcd(val, v2);
if(v3) val *= v3 / __gcd(val, v3);
if(v4) val *= v4 / __gcd(val, v4);
return val;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n; init();
if(n == 2){
cout << "4 7" << "\n" << "23 10";
return 0;
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(!((i + j) & 1)){
if(b[i - 1][j - 1]) b[i][j] = b[i - 1][j - 1];
else b[i][j] = ++cnt;
}
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(!((i + j) & 1)){
if(c[i - 1][j + 1]) c[i][j] = c[i - 1][j + 1];
else c[i][j] = ++cnt;
a[i][j] = prime[b[i][j]] * prime[c[i][j]];
}
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(((i + j) & 1)) a[i][j] = LCM(a[i - 1][j], a[i + 1][j], a[i][j - 1], a[i][j + 1]) + 1;
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cout << a[i][j] << " ";
}
cout << "\n";
}
return 0;
}
P7207 [COCI2019-2020#3] Sob
首先观察样例并打出二进制表,可以猜测我们要将 \(a\) 中一段区间丢进 \(b\) 里进行匹配。进一步观察(啥都发现不了),可以发现它应该是一个后缀丢到前缀的形式。但是是哪个数组的前缀?这里有一个很 \(\rm nb\) 的思路:观察到题目中给的结论:“对于任意 \(n, m\) 均有解”,于是考虑将规模为 \((n, m)\) 的配对问题递归到 \((n - x, m + x)\) 规模的问题。
那么我们要找到一个 \(b\) 中的前缀并跟 \(a\) 中的后缀进行匹配,考虑如何判断 \([n - len, n - 1]\) 和 \([m, m + len - 1]\) 是否匹配,显然有一个必要条件是 \((m + len - 1) \& (n - 1) = n - 1\)。但是实际上,通过观察可以发现,这个必要条件也是充分的!这是因为对于减法影响到最高的 \(1\) 位显然都是一样的,而对于 \(0\) 位对减法是没有影响的,因此我们可以找到一个最小的 \(len\) 并划分到子问题解决。
qwq
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
int n, m;
int lowbit(int x){return x & -x;}
void solve(int len){ // has trucked
if(len == n) return;
int eda = n - len - 1, stb = m + len, ans = 0;
for(int i = stb; i <= m + n - 1; i++){
if((i & eda) == eda) {ans = i - stb + 1; break;}
}
for(int i = stb; i <= stb + ans - 1; i++) cout << (eda - (stb + ans - 1 - i)) << " " << i << "\n";
// cout <<":"<< len << " " << ans << "\n";
solve(len + ans);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n >> m; solve(0);
return 0;
}