10.6 模拟赛题解报告
T1
题目描述
给定一个 \(x\),找一个最大的小于等于 \(x\) 的一个数,并且满足这个数的每一位的数是单调不降的。
\(x\leq 10^{10^5}\)
solution
直接贪心就好了,\(O(|x的位数|)\) 扫一遍序列,如果碰见不合法的就将满足条件的最后那个数第一次出现的位置 \(-1\),然后后面的数全置为 \(9\) 就好了。
复杂度 \(O(n)\)
坑点
-
注意前导 \(0\), 给一组样例:1097
-
不要用 \(getline\) 读入。(好多学弟好像都因为这个挂 0 了)
-
要特判 \(0\) (众人掉坑)
/*
work by:Ariel_
Knowledge:
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
const int MAXN = 1e5 + 5;
int read(){
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
return x * f;
}
int a[MAXN], n, fag, num, pre, ne, p;
char s[MAXN];
int main(){
//freopen("increase.in", "r", stdin);
//freopen("increase.out", "w", stdout);
scanf("%s", s + 1);
n = strlen(s + 1);
if(n == 1 && a[1] == 0) {cout<<"0";return 0;}
for (int i = 1; i <= n; i++) a[i] = s[i] - '0';
int last = 1;
for (int i = 2; i <= n; i++) {
if(a[i] == a[last]) continue;
if(a[i] > a[last]) {last = i; continue;}
if(a[i] < a[last]) {
a[last]--, ne = 1, fag = last + 1;
break;
}
}
if(!ne) {
for (int i = 1; i <= n; i++) printf("%d", a[i]);
return 0;
}
for (int i = 1; i <= n; i++) {
if(i == 1 && a[i] == 0 && last == 1) continue;
if(i >= fag && fag && ne) cout<<9;
else cout<<a[i];
}
return 0;
}
T2
题目描述
给定一个长为 \(n\) 的序列,每个数为 \(a_i\)。
求 \(\sum_{l = 1}^{l = n}\sum_{r = l}^{r = n}\sum_{i = l}^{i = r}\sum^{r}_{j = i + 1, a_j < a_i} a_i\times a_j\)
\(n \leq 4\times 10^4, a_i \leq 10^{12}\)
solution
60pts
直接抄式子就好。
好多分哇/se
80pts
不难发现,每一个逆序对在一个区间内只会产生一次贡献,求出每个逆序对所在的区间数量,然后乘以这个逆序对的贡献就好了。
100pts
离散化,树状数组。
\(a_i, a_j\) 的贡献就是 \(a_i \times a_j \times i\times (n - j + 1)\)
移一下式子得到 \((a_i \times i)(a_j\times(n - j + 1))\)
树状数组内维护 \(a_i \times i\) ,在求逆序对的时候求一下这个玩意的和,然后乘以 \(a_j \times (n - j + 1)\) 就是 \(a_j\) 能与前面的数的逆序对产生的贡献。
龟速乘
复杂度 \(O(nlogn)\)
/*
work by:Ariel_
Knowledge:
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#define int long long
using namespace std;
const int MAXN = 1e5 + 5;
const int mod = 1e12 + 7;
int read(){
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
return x * f;
}
int tree[MAXN], n, a[MAXN], tmp[MAXN], b[MAXN], Ans, cnt;
void Modify(int x, int k) {
for (int i = x; i <= cnt; i += i & (-i)) tree[i] = (tree[i] + k) % mod;
}
int Query(int x) {
int ret = 0;
for (int i = x; i; i -= i & (-i)) ret = (ret + tree[i]) % mod;
return ret;
}
int Mul(int x, int p) {
int res = 0;
while(p) {
if(p & 1) res = (res + x) % mod;
x = (x + x) % mod, p >>= 1;
}
return res;
}
signed main(){
//freopen("multiplication.in", "r", stdin);
//freopen("multiplication.out", "w", stdout);
n = read();
for (int i = 1; i <= n; i++) a[i] = tmp[i] = read();
sort(tmp + 1, tmp + n + 1);
cnt = unique(tmp + 1, tmp + n + 1) - tmp - 1;
for (int i = 1; i <= n; i++) b[i] = lower_bound(tmp + 1, tmp + cnt + 1, a[i]) - tmp;
for(int i = 1; i <= n; i++) {
int ret = (Query(cnt) - Query(b[i]) + mod) % mod;
Ans = (Ans + Mul(Mul(ret, a[i]), n - i + 1)) % mod;
Modify(b[i], Mul(a[i], i));
}
printf("%lld\n", (Ans + mod) % mod);
return 0;
}
T3
题目大意
给定 \(n\) 个矩形,每个矩形的左下角坐标为 \((a_i, b_i)\), 右上角的坐标为 \((c_i, d_i)\),判断这 \(n\) 个矩形是否能够恰好拼成一个大矩形,每个小矩形不重复。
\(n\leq 50000, |a_i|, |b_i|, |c_i|, |d_i| \leq 10^9\)
solution
30pts
\(n^2\)
首先所有小矩形的面积和一定等于大矩形的面积。
然后 \(n^2\) 判断每两个小矩形是否能够相互覆盖。
100pts
思路题,大的矩形内部除了四个顶点可能出现 1 个点其余地方都是 2 个或者 4 个点。
/*
work by:Ariel_
Knowledge:
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<map>
using namespace std;
const int INF = 1e9;
const int MAXN = 50000 + 5;
int read(){
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
return x * f;
}
int n, a[MAXN], b[MAXN], c[MAXN], d[MAXN], T, Max_x = -INF, Max_y = -INF, Min_x = INF, Min_y = INF;
map<int, map<int, int> >mp;
void clear() {
memset(a, 0, sizeof a), memset(b, 0, sizeof b);
memset(c, 0, sizeof c), memset(d, 0, sizeof d);
Max_x = -INF, Max_y = -INF, Min_x = INF, Min_y = INF;
}
void get(int i){
Min_x = min(Min_x, a[i]), Max_x = max(Max_x, c[i]);
Min_y = min(Min_y, b[i]), Max_y = max(Max_y, d[i]);
}
bool work(){
int S = 0;
for (int i = 1; i <= n; i++) {
get(i);
S += (c[i] - a[i]) * (d[i] - b[i]);
}
if(S != (Max_x - Min_x) * (Max_y - Min_y)) return 0;
int num = 0;
for (int i = 1; i <= n; i++) {
mp[a[i]][b[i]]++, mp[a[i]][d[i]]++;
mp[c[i]][b[i]]++, mp[c[i]][d[i]]++;
}
for (int i = 1; i <= n; i++) {
if(mp[a[i]][b[i]] != 2 && mp[a[i]][b[i]] != 4) num++;
if(mp[a[i]][d[i]] != 2 && mp[a[i]][d[i]] != 4) num++;
if(mp[c[i]][b[i]] != 2 && mp[c[i]][b[i]] != 4) num++;
if(mp[c[i]][d[i]] != 2 && mp[c[i]][d[i]] != 4) num++;
}
return num == 4;
}
int main(){
T = read();
while(T--) {
clear();
n = read();
mp.clear();
for (int i = 1; i <= n; i++)
a[i] = read(), b[i] = read(), c[i] = read(), d[i] = read();
if(work()) puts("Perfect");
else puts("Guguwansui");
}
return 0;
}