模擬賽簡要題解與心得
目錄
- 模擬賽簡要題解與心得
- T1 方格紙與直線
- 題解
- T2 探險
- 題解
- 3.蘋果樹
- 題解
- 心得
模擬賽簡要題解與心得
最近我的心態炸了,寫此博客紀念調侃一下.
T1 方格紙與直線
【題目描述】
小林有一張 n 行 m 列的方格紙,如下所示。
該方格紙黑白相間,且第一行第一列為黑色。頑皮的亮亮在方格紙上畫了一
條連接左上角和右下角的線段。小林看到方格紙後,馬上算出了位於黑色區域的
線段的長度之和占整條線段長度的比值。現在,他想考考你會不會算。
【輸入格式】
一行兩個整數 n 和 m。
【輸出格式】
輸出一個分數,即題目中所求的比值,用兩個由’/’分隔的互質整數表示。
【樣例輸入】
4 6
【樣例輸出】
【數據規模】
對於 50%的數據,n, m <= 10^6;
對於 100%的數據,1 <= n,m <= 10^9。
題解
找規律的好題,但是還是因為時間問題錯失了滿分的機會.
題目要求的是比值,故當 m 與 n 不互質時,我們可以求出 m 和 n 的最大
公約數 d,並將 m /= d, n /= d,並不影響結果。故我們現在假定 m 和 n 互質。若m 和 n 中有一個為偶數, 那麽根據對稱性, 答案就是 1/2。 如果 m 和 n 均為奇數,
那麽答案就是\((n*m+1) / (2*m*n)\)。
T2 探險
【題目描述】
小林和亮亮來到森林中探險, 森林中有一條長度為 S 的小路 (編號從 1 到 S) ,
小林則關心在某一位置的視野。若位置 x 有濃霧,則位置 x 的視野為 0。若
從 x 一直到 S 或從 x 一直到 1 全都沒有濃霧,則視野為INF。其他情況下,位置x的視野為\(max{R - L + 1}\)
要滿足這個區間內沒有濃霧的產生.
具體來說,會有以下事件發生:
1、“1 L R”小路的[L, R]部分產生了濃霧;
2、“2 L R”小路的[L, R]部分濃霧散去了。
3、“3 X” 查詢 X 點的視野。
一開始,小路上沒有任何濃霧。
【輸入格式】
第一行一個整數,為小路的長度 S。
第二行一個整數,為事件數 Q。
接下來 Q 行,每行一個事件,格式如題目描述。
對於每一個詢問事件,輸出一個整數或一行字符串“INF”,代表所求視野。
【樣例輸入】
5
5
1 2 4
3 1
3 4
2 3 3
3 3
【樣例輸出】
INF
0
1
【數據規模】
對於 40%的數據,SQ <= 510^7。
對於 100%的數據,2≤S≤100,000,2≤Q≤200,000,1≤L≤R≤S,1≤X≤S。
題解
T1推了很長時間,忘記找規律了,結果T2考場一眼出正解卻不敢寫.寫了分塊.不知道怎麽的就過了.雖然過了,花了我1.5h的時間,so sad.
正解:線段樹,維護區間的和.如果這個點是霧,則這個點的值為1.然後二分最遠到達的l,r即可.
放上分塊的代碼吧:
CODE:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxN = 100000 + 7;
int a[maxN],belong[maxN],R[maxN],L[maxN];
int sum[maxN];
int vis[maxN];
int n;
inline 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;
}
void pushdown(int now) {
if(vis[now] == 1) {
for(int i = L[now];i <= R[now];++ i) a[i] = 1;
sum[now] = R[now] - L[now] + 1;
}
if(vis[now] == 2) {
for(int i = L[now];i <= R[now];++ i) a[i] = 0;
sum[now] = 0;
}
vis[now] = 0;
return ;
}
inline void change_done(int l,int r) {
int belong_l = belong[l],belong_r = belong[r];
if(belong_l == belong_r) {
if(vis[belong_l]) pushdown(belong_l);
for(int i = l;i <= r;++ i) a[i] = 1;
sum[belong_l] = 0;
for(int i = L[belong_l];i <= R[belong_l];++ i)
if(a[i]) sum[belong_l] ++;
return;
}
for(int i = belong_l + 1;i < belong_r;++ i) vis[i] = 1,sum[i] = R[i] - L[i] + 1;
if(vis[belong_l]) pushdown(belong_l);
if(vis[belong_r]) pushdown(belong_r);
for(int i = l;i <= R[belong_l];++ i) a[i] = 1;
for(int i = L[belong_r];i <= r;++ i) a[i] = 1;
sum[belong_l] = sum[belong_r] = 0;
for(int i = L[belong_l];i <= R[belong_l];++ i)
if(a[i]) sum[belong_l] ++;
for(int i = L[belong_r];i <= R[belong_r];++ i)
if(a[i]) sum[belong_r] ++;
return ;
}
inline void change_clear(int l,int r) {
int belong_l = belong[l],belong_r = belong[r];
if(belong_l == belong_r) {
if(vis[belong_l]) pushdown(belong_l);
for(int i = l;i <= r;++ i) a[i] = 0;
sum[belong_l] = 0;
for(int i = L[belong_l];i <= R[belong_l];++ i)
if(a[i]) sum[belong_l] ++;
return;
}
for(int i = belong_l + 1;i < belong_r;++ i) vis[i] = 2,sum[i] = 0;
if(vis[belong_l]) pushdown(belong_l);
if(vis[belong_r]) pushdown(belong_r);
for(int i = l;i <= R[belong_l];++ i) a[i] = 0;
for(int i = L[belong_r];i <= r;++ i) a[i] = 0;
sum[belong_l] = sum[belong_r] = 0;
for(int i = L[belong_l];i <= R[belong_l];++ i)
if(a[i]) sum[belong_l] ++;
for(int i = L[belong_r];i <= R[belong_r];++ i)
if(a[i]) sum[belong_r] ++;
return ;
}
inline int query(int pos) {
int Le = pos,Ri = pos;
int belong_pos = belong[pos];
if(vis[belong_pos]) pushdown(belong_pos);
if(a[pos]) {return 0;}
if(pos == n || pos == 1) return -1;
for(int i = pos;i <= R[belong_pos];++ i) {
if(!a[i]) Ri = i;
else break;
}
for(int i = pos - 1;i >= L[belong_pos];-- i) {
if(!a[i]) Le = i;
else break;
}
int belong_end = belong[n],belong_begin = belong[1];
if(vis[belong[Ri + 1]]) pushdown(belong[Ri + 1]);
if(!a[Ri + 1]) {
for(int j = belong_pos + 1;j <= belong_end;++ j) {
if(sum[j] != 0) break;
Ri = R[j];
}
if(vis[belong[Ri + 1]]) pushdown(belong[Ri + 1]);
for(int j = Ri + 1;j <= n;++ j) {
if(!a[j]) Ri = j;
else break;
}
}
if(vis[belong[Le - 1]]) pushdown(belong[Le - 1]);
if(!a[Le - 1]) {
for(int j = belong_pos - 1;j >= belong_begin;-- j) {
if(sum[j] != 0) break;
Le = L[j];
}
if(vis[belong[Le - 1]]) pushdown(belong[Le - 1]);
for(int j = Le - 1;j >= 1;-- j) {
if(!a[j]) Le = j;
else break;
}
}
if(Ri == n || Le == 1) return -1;
return Ri - Le + 1;
}
int main() {
freopen("explore.in","r",stdin);
freopen("explore.out","w",stdout);
n = read();
int m = read(),type,l,r,x;
int q = sqrt(n);
for(int i = 1;i <= n;++ i) belong[i] = i / q + 1;
for(int i = 1;i <= n;++ i) R[belong[i]] = i;
for(int i = n;i >= 1;-- i) L[belong[i]] = i;
while(m --) {
type = read();
if(type == 1) {
l = read();r = read();
change_done(l,r);
}
if(type == 2) {
l = read();r = read();
change_clear(l,r);
}
if(type == 3) {
x = read();
int tmp = query(x);
if(tmp == -1) {puts("INF");continue;}
printf("%d\n", tmp);
}
}
fclose(stdin);
fclose(stdout);
return 0;
}
線段樹代碼(STD):
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#define fortodo(i, f, t) for (i = f; i <= t; i++)
using namespace std;
int lsd[200001], rsd[200001], lsid[200001], rsid[200001], cov[200001], segsize;
bool emp[200001];
int SEG_Build(int L, int R)
{
int Nid = ++segsize;
lsd[Nid] = L; rsd[Nid] = R;
emp[Nid] = true; cov[Nid] = 0;
if (L == R)
lsid[Nid] = rsid[Nid] = -1;
else
{
lsid[Nid] = SEG_Build(L, (L + R) / 2);
rsid[Nid] = SEG_Build((L + R) / 2 + 1, R);
};
return Nid;
};
bool SEG_Empty(int Nid)
{
if (cov[Nid] == 0) return true;
if (cov[Nid] == 1) return false;
return emp[Nid];
};
void SEG_Reemp(int Nid)
{
emp[Nid] = SEG_Empty(lsid[Nid]) && SEG_Empty(rsid[Nid]);
};
void SEG_Inherit(int Nid)
{
if (cov[Nid] == -1) return;
if (lsd[Nid] == rsd[Nid]) return;
cov[lsid[Nid]] = cov[Nid];
cov[rsid[Nid]] = cov[Nid];
cov[Nid] = -1;
SEG_Reemp(Nid);
};
void SEG_Paint(int Nid, int L, int R, int Color)
{
SEG_Inherit(Nid);
if ((L == lsd[Nid]) && (R == rsd[Nid]))
{
cov[Nid] = Color;
return;
};
int Div = (lsd[Nid] + rsd[Nid]) / 2;
if (L > Div) SEG_Paint(rsid[Nid], L, R, Color);
if (R <= Div) SEG_Paint(lsid[Nid], L, R, Color);
if ((L <= Div) && (R > Div))
{
SEG_Paint(lsid[Nid], L, Div, Color);
SEG_Paint(rsid[Nid], Div + 1, R, Color);
};
SEG_Reemp(Nid);
};
bool SEG_Query(int Nid, int L, int R)
{
SEG_Inherit(Nid);
if (SEG_Empty(Nid)) return true;
if ((L == lsd[Nid]) && (R == rsd[Nid])) return SEG_Empty(Nid);
int Div = (lsd[Nid] + rsd[Nid]) / 2;
if (L > Div) return SEG_Query(rsid[Nid], L, R);
if (R <= Div) return SEG_Query(lsid[Nid], L, R);
return SEG_Query(lsid[Nid], L, Div) && SEG_Query(rsid[Nid], Div + 1, R);
};
int S, Q;
int i, j;
int Opt, X, Y;
void Answer(int P)
{
if (!SEG_Query(1, P, P))
{
printf("0\n");
return;
};
if ((SEG_Query(1, 1, P)) || (SEG_Query(1, P, S)))
{
printf("INF\n");
return;
};
int L, R, M, Ans[2];
L = 2; R = P;
while (L < R)
{
M = (L + R) / 2;
if (SEG_Query(1, M, P))
R = M;
else
L = M + 1;
};
Ans[0] = L;
L = P; R = S - 1;
while (L < R)
{
M = (L + R + 1) / 2;
if (SEG_Query(1, P, M))
L = M;
else
R = M - 1;
};
Ans[1] = L;
printf("%d\n", Ans[1] - Ans[0] + 1);
};
int main()
{
freopen("explore.in", "r", stdin);
freopen("explore.out", "w", stdout);
scanf("%d%d", &S, &Q);
segsize = 0;
SEG_Build(1, S);
fortodo(i, 1, Q)
{
scanf("%d%d", &Opt, &X);
if (Opt != 3) scanf("%d", &Y);
if (Opt == 1) SEG_Paint(1, X, Y, 1);
if (Opt == 2) SEG_Paint(1, X, Y, 0);
if (Opt == 3) Answer(X);
};
return 0;
};
3.蘋果樹
【題目描述】
小林有一棵蘋果樹,樹上一共有 n 個節點,n-1 條邊,每條邊都有長度,且
有些節點上結有蘋果。
亮亮希望砍掉蘋果樹的某些邊,使得沒有任意兩個蘋果在同一聯通塊中,並
且所砍去的邊的長度之和最小。
【輸入格式】
第一行兩個整數 n, k, 分別表示樹的結點數和含有蘋果的結點數。 結點用 0 ~
n-1 標號。
接下來 n-1 行,每行三個數 x, y, z,表示一條從 x 到 y 權值為 z 的邊。
接下來 k 行,每行一個數 x,表示編號為 x 的結點上結有一個蘋果。
【輸出格式】
只有一個整數,表示最小的長度之和。
【樣例輸入】
5 3
2 1 7
1 0 4
2 4 9
1 3 4
0
1
2
【樣例輸出】
11
【數據規模】
對於 40%的數據,n <= 20;
其中 10%的數據和另外 20%的數據,樹的形態為一條鏈;
對於 100%的數據,2 <= n <= 100000,2 <= k <= n, 1 <= 邊權 <= 1000000。
題解
實在是沒有時間寫暴力了,不然40的數據\(2^n\)枚舉子集.再\(n\)判斷聯通塊.
\(60\)的數據直接線段樹即可.
剩下的應該是一個樹形D.P
實在是沒有時間了.
樹形 DP。首先任取一個有蘋果的結點為根,構建整棵樹,接下來自底向上進行動態
規劃。設 f[u]表示以 u 為根的子樹中蘋果互不連通,且子樹中的蘋果不與樹外的蘋果連通的
最小代價。g[u]表示以 u 為根的子樹中蘋果互不連通所需的最小代價。
那麽當 u 上有蘋果時,
g[u] = sum (f[v]) (v 為 u 的所有子節點) ;
f[u] = g[u] + pre[u] (pre[u]為 u 與其父節點連邊的權值) 。
當 u 上無蘋果時,
設 s[u] = sum (f[v]) (v 為 u 的所有子節點)
則 g[u] = min (s[u] - f[v] + g[v])
f[u] = min(s[u], g[u] + pre[u)]
心得
考場策略出現問題,T1沒有找規律,T2一眼題沒有寫正解,T3沒有時間看.
開場肝T1.
直接sb了.
以後模擬賽時應該通讀一遍題目.
希望以後不犯類似的錯誤.
模擬賽簡要題解與心得