GRYZ20211028模拟赛解题报告
写在前面
期望得分:\(100+100+30 \sim 60 = 230 \sim 260pts\)
实际得分:\(90 + 96 + 50 = 236pts\)
(第二题十二个测试点,每个测试点 \(8pts\))
今天轮到三区出题了,龙又搬了他那不知几年前搞的题。
也可能是感觉我们前两天考的太烂于是换了套简单的(((
T1
你发现出现次数 \(\ge \frac{n+1}{2}\) 的数不会太多,然后你直接离散化统计出现次数找到那几个出现次数 \(\ge \frac{n+1}{2}\) 的数,暴力判断需要翻转几次,对次数取 \(\min\) 即可。
为什么挂了 10pts ?因为他这个点卡快读 /fn/fn
/*
Work by: Suzt_ilymtics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 6e5+500;
const int INF = 1e9+7;
const int mod = 1e9+7;
struct node { int x, y; }a[MAXN];
int n, ans, res;
int date[MAXN << 1], Cnt = 0, date_num = 0;
int cnt[MAXN << 1];
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 - '0' , ch = getchar();
return f ? -s : s;
}
int main()
{
freopen("card.in","r",stdin);
freopen("card.out","w",stdout);
scanf("%d", &n); ans = INF;
for(int i = 1; i <= n; ++i) {
scanf("%d%d", &a[i].x, &a[i].y);
date[++date_num] = a[i].x, date[++date_num] = a[i].y;
}
sort(date + 1, date + date_num + 1), date[0] = -INF;
for(int i = 1; i <= date_num; ++i) if(date[i] != date[i - 1]) date[++Cnt] = date[i];
for(int i = 1; i <= n; ++i) {
a[i].x = lower_bound(date + 1, date + Cnt + 1, a[i].x) - date;
a[i].y = lower_bound(date + 1, date + Cnt + 1, a[i].y) - date;
}
for(int i = 1; i <= n; ++i) {
if(a[i].x == a[i].y) cnt[a[i].x] ++;
else cnt[a[i].x] ++, cnt[a[i].y] ++;
}
for(int i = 1; i <= Cnt; ++i) {
if(cnt[i] >= (n + 1) / 2) {
int res = 0;
for(int j = 1; j <= n; ++j) {
if(a[j].x == i) {
res ++;
}
}
ans = min(ans, max((n + 1) / 2 - res, 0));
}
}
if(ans == INF) puts("Impossible");
else printf("%d\n", ans);
fclose(stdin);
fclose(stdout);
return 0;
}
T2
首先你要知道这个交换相邻元素使序列排好序是让你求逆序对。
然后你考虑确定出这几个子串的大小关系。
考虑自定义一个 cmp 直接排序,因为空间关系,我们要对起始位置排序而不是把每个子串列出来排序。
时间复杂度 \(\mathcal O(nm \log n)\)。
考虑如何优化。
发现瓶颈在于比较两个串的大小关系,然后你考虑 Hash + ST 表,然后你就可以在 \(\log m\) 的时间内比较两个串的大小关系了。
时间复杂度 \(\mathcal O(n \log m \log n)\)。
ST 表几乎没用过,但它并没有让我调太多时间,这很好。
/*
Work by: Suzt_ilymtics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define int long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 2e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
const int base = 19260817;
int n, m, ans = 0;
int a[MAXN], c[MAXN];
int Pow[MAXN], st[MAXN][18];
char s[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 - '0' , ch = getchar();
return f ? -s : s;
}
namespace Bit {
int sum[MAXN];
int lb(int x) { return x & -x; }
void Modify(int x, int k) { for(; x <= n; x += lb(x)) sum[x] += k; }
int Query(int x) { int res = 0; for(; x; x -= lb(x)) res += sum[x]; return res; }
}
bool cmp(int x, int y) {
int len = 0;
for(int j = 16; j >= 0; --j) {
if(x + (1 << j) > n || y + (1 << j) > n) continue;
if(st[x + len][j] == st[y + len][j]) {
len = len + (1 << j);
}
}
if(len >= m) return false;
for(int i = len + 1; i <= m; ++i) {
if(s[x + i - 1] < s[y + i - 1]) return true;
if(s[x + i - 1] > s[y + i - 1]) return false;
}
return false;
}
bool Check(int l, int r) {
int len = 0;
for(int j = 16; j >= 0; --j) {
if(l + (1 << j) > n || r + (1 << j) > n) continue;
if(st[l + len][j] == st[r + len][j]) {
len = len + (1 << j);
}
}
if(len >= m) return true;
else return false;
}
signed main()
{
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
n = read(), m = read();
cin >> s + 1;
Pow[0] = 1;
for(int i = 1; i <= n + m; ++i) Pow[i] = Pow[i - 1] * base % mod;
for(int i = 1; i <= m; ++i) s[n + i] = '1';
s[n + m + 1] = '\0';
for(int i = 1; i <= n; ++i) {
st[i][0] = s[i];
}
for(int i = 1; i <= 16; ++i) {
for(int j = 1; j <= n; ++j) {
st[j][i] = st[j][i - 1] * Pow[(1 << i - 1)] % mod + st[j + (1 << i - 1)][i - 1];
st[j][i] %= mod;
// cout<<i<<" "<<j<<' '<<st[j][i]<<"\n";
}
}
for(int i = 1; i <= n; ++i) a[i] = i;
sort(a + 1, a + n + 1, cmp);
c[a[1]] = 1;
for(int i = 2; i <= n; ++i) {
if(Check(a[i], a[i - 1])) c[a[i]] = c[a[i - 1]];
else c[a[i]] = i;
}
for(int i = n; i >= 1; --i) {
int res = Bit::Query(c[i] - 1);
ans = ans + res;
Bit::Modify(c[i], 1);
}
printf("%lld\n", ans);
fclose(stdin);
fclose(stdout);
return 0;
}
T3
用 set 维护每一块巧克力然后爆搜切哪 \(k-1\) 刀。
时间复杂度 \(\mathcal O(\text{巨大})\)。
反正大样例跑了两个半小时都没跑完。
实测只能过 20 pts ?还有两个点 WA 掉了。
然后你又想了另外一种方法,你先预处理出每个 \(a_i\) 能被那些矩形所拼。
然后你 dfs 每个 \(a_i\) 用哪个矩形,这样就做有 50pts 的好成绩。还是有两个点 WA 掉了,很奇怪(和上面 WA 掉的两个点不一样)
正解是记忆化搜索,设 \(f[sx][sy][ex][ey][S]\) 表示 \((sx,sy) - (ex, ey)\) 这块矩形能否组成的一部分 \(a_i\) 的组成的集合 \(S\)。
20pts Code
/*
Work by: Suzt_ilymtics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
struct node {
int lx, ly, rx, ry;
bool operator < (const node &b) const {
if(lx != b.lx) return lx < b.lx;
if(ly != b.ly) return ly < b.ly;
if(rx != b.rx) return rx < b.rx;
return ry < b.ry;
}
};
int n, m, K;
int a[14][14], s[14][14], b[20];
int cnt[1010];
bool vis[1010], flag[1010], Flag = false;
multiset<node> S;
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 - '0' , ch = getchar();
return f ? -s : s;
}
int Calc(int lx, int ly, int rx, int ry) {
return s[rx][ry] - s[lx - 1][ry] - s[rx][ly - 1] + s[lx - 1][ly - 1];
}
void dfs(int pos) {
if(pos == K) {
// cout<<pos<<"\n";
int res = 0;
for(multiset<node>::iterator it = S.begin(); it != S.end(); ++it) {
int x = Calc(it->lx, it->ly, it->rx, it->ry);
if(!flag[x] && vis[x]) flag[x] = true, res++;
}
for(multiset<node>::iterator it = S.begin(); it != S.end(); ++it) {
int x = Calc(it->lx, it->ly, it->rx, it->ry);
flag[x] = false;
}
if(res == K) Flag = true;
return ;
}
multiset<node> E = S;
for(multiset<node>::iterator it = E.begin(); it != E.end(); ++it) {
node x = *it;
S.erase(x);
for(int i = x.lx; i < x.rx; ++i) {
S.insert((node){x.lx, x.ly, i, x.ry});
S.insert((node){i + 1, x.ly, x.rx, x.ry});
dfs(pos + 1);
S.erase((node){x.lx, x.ly, i, x.ry});
S.erase((node){i + 1, x.ly, x.rx, x.ry});
if(Flag) return ;
}
for(int i = x.ly; i < x.ry; ++i) {
S.insert((node){x.lx, x.ly, x.rx, i});
S.insert((node){x.lx, i + 1, x.rx, x.ry});
dfs(pos + 1);
S.erase((node){x.lx, x.ly, x.rx, i});
S.erase((node){x.lx, i + 1, x.rx, x.ry});
if(Flag) return ;
}
S.insert(x);
}
}
int main()
{
freopen("chocolate.in","r",stdin);
freopen("chocolate.out","w",stdout);
int T = read();
while(T--) {
S.clear(); Flag = false;
bool fjh = false;
n = read(), m = read(), K = read();
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
a[i][j] = read();
if(a[i][j] != 1) fjh = true;
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
}
}
memset(vis, false, sizeof vis);
for(int i = 1; i <= K; ++i) b[i] = read(), vis[b[i]] = true;
int sum = 0;
for(int i = 1; i <= K; ++i) sum = sum + b[i];
if(n == 1 && !fjh) {
if(sum == m) puts("yes");
else puts("no");
continue;
}
if(sum != s[n][m]) {
puts("no");
continue;
}
S.insert((node){1, 1, n, m});
cnt[s[n][m]]++;
dfs(1);
if(Flag) puts("yes");
else puts("no");
}
fclose(stdin);
fclose(stdout);
return 0;
}
50pts Code
/*
Work by: Suzt_ilymtics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
struct node {
int sx, sy, ex, ey;
};
int n, m, K;
int a[20][20], s[20][20];
int b[22];
bool Flag, vis[20][20];
vector<node> V[20];
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 - '0' , ch = getchar();
return f ? -s : s;
}
int Calc(int lx, int ly, int rx, int ry) {
return s[rx][ry] - s[lx - 1][ry] - s[rx][ly - 1] + s[lx - 1][ly - 1];
}
void dfs(int pos) {
if(pos == K + 1) {
Flag = true;
return ;
}
for(int i = 0, M = V[pos].size(); i < M; ++i) {
bool flag = false;
int sx = V[pos][i].sx, ex = V[pos][i].ex, sy = V[pos][i].sy, ey = V[pos][i].ey;
for(int j = sx; j <= ex; ++j) {
for(int k = sy; k <= ey; ++k) {
if(vis[j][k]) {
flag = true;
break;
}
}
if(flag) break;
}
if(!flag) {
for(int j = sx; j <= ex; ++j) {
for(int k = sy; k <= ey; ++k) {
vis[j][k] = true;
}
}
dfs(pos + 1);
if(Flag) return ;
for(int j = sx; j <= ex; ++j) {
for(int k = sy; k <= ey; ++k) {
vis[j][k] = false;
}
}
}
}
}
int main()
{
freopen("chocolate.in","r",stdin);
freopen("chocolate.out","w",stdout);
int T = read();
while(T--) {
n = read(), m = read(), K = read();
bool fjh = false;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j<= m; ++j) {
a[i][j] = read();
if(a[i][j] != 1) fjh = true;
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
}
}
int sum = 0;
for(int i = 1; i <= K; ++i) {
b[i] = read();
sum = sum + b[i];
V[i].clear();
for(int sx = 1; sx <= n; ++sx) {
for(int sy = 1; sy <= m; ++sy) {
for(int ex = sx; ex <= n; ++ex) {
for(int ey = sy; ey <= m; ++ey) {
int x = Calc(sx, sy, ex, ey);
if(x == b[i]) V[i].push_back((node){sx, sy, ex, ey});
}
}
}
}
}
if(n == 1 && !fjh) {
if(sum == s[n][m]) puts("yes");
else puts("no");
continue;
}
if(sum < s[n][m]) {
puts("no");
continue;
}
Flag = false;
memset(vis, false, sizeof vis);
dfs(1);
if(!Flag) puts("no");
else puts("yes");
}
fclose(stdin);
fclose(stdout);
return 0;
}
其实我觉得两个部分分代码放着没什么必要,就是恶心恶心人(((