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;
}
其實我覺得兩個部分分程式碼放著沒什麼必要,就是噁心噁心人(((