模拟赛简要题解与心得
最近我的心态炸了,写此博客纪念调侃一下.
T1 方格纸与直线
【题目描述】
小林有一张 n 行 m 列的方格纸,如下所示。
该方格纸黑白相间,且第一行第一列为黑色。顽皮的亮亮在方格纸上画了一
条连接左上角和右下角的线段。小林看到方格纸后,马上算出了位于黑色区域的
线段的长度之和占整条线段长度的比值。现在,他想考考你会不会算。
【输入格式】
一行两个整数 n 和 m。
【输出格式】
输出一个分数,即题目中所求的比值,用两个由’/’分隔的互质整数表示。
【样例输入】
4 6
【样例输出】
1/2
【数据规模】
对于 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
", 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
");
return;
};
if ((SEG_Query(1, 1, P)) || (SEG_Query(1, P, S)))
{
printf("INF
");
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
", 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了.
以后模拟赛时应该通读一遍题目.
希望以后不犯类似的错误.