前言
- 本博客中的代码大多不是正解(因为作者太蒻了),本博客侧重根据测试点规模骗分方法的分析。也许以后作者能力上来了,会补上正解。内容浅显,文笔拙劣,望dalao轻喷
T1 玩具谜题
参考代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
struct node
{
int aspect;
string job;
}info[MAXN];
struct command
{
int aspect,cnt;
}cmd[MAXN];
int n,m;
int command[MAXN][2];
void init()
{
freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
for (int i = 1;i <= n;++i)
{
qread(info[i].aspect);
getline(cin,info[i].job);
}
for (int i = 1;i <= m;++i)
{
qread(cmd[i].aspect);
qread(cmd[i].cnt);
}
}
void work()
{
int cur = 1,nxt = 0;
for (int i = 1;i <= m;++i)
{
int h = cmd[i].cnt;
if (info[cur].aspect == 0)
{
if (cmd[i].aspect == 0)
{
nxt = cur - h;
if (nxt <= 0)
{
nxt += n;
}
}
else
{
nxt = cur + h;
if (nxt > n)
{
nxt = nxt - n;
}
}
}
else
{
if (cmd[i].aspect == 0)
{
nxt = cur + h;
if (nxt > n)
{
nxt = nxt - n;
}
}
else
{
nxt = cur - h;
if (nxt <= 0)
{
nxt += n;
}
}
}
cur = nxt;
}
cout << info[cur].job;
}
int main()
{
init();
work();
return 0;
}
分析
模拟,分析略。
T2 天天爱跑步
参考代码(60pts)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100000;
struct edge
{
int to,nxt;
}info[MAXN << 1];
int n,m,e,ui,vi;
int head[MAXN],appear[MAXN],s[MAXN],t[MAXN],cntPlayer[MAXN][2],ans[MAXN];
bool inQueryStack[MAXN];
vector<int> query[MAXN];
inline void addedge(int from,int to,int wgt)
{
info[++e].to = to;
info[e].nxt = head[from];
head[from] = e;
}
inline void addedge(int from,int to)
{
info[++e].to = to;
info[e].nxt = head[from];
head[from] = e;
}
void init()
{
freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
for (int i = 1;i <= n - 1;++i)
{
qread(ui);
qread(vi);
addedge(ui,vi);
addedge(vi,ui);
}
for (int i = 1;i <= n;++i) qread(appear[i]);
for (int i = 1;i <= m;++i)
{
qread(s[i]);
qread(t[i]);
cntPlayer[s[i]][0]++;
cntPlayer[t[i]][1]++;
}
}
void dfs(int u,int f,int d)
{
for (int i = head[u];i;i = info[i].nxt)
{
int v = info[i].to;
if (v == f) continue;
dfs(v,u,d + 1);
cntPlayer[u][1] += cntPlayer[v][1];
}
if (d == appear[u]) ans[u] += cntPlayer[u][1];
}
void dfs2(int u,int f,int d)
{
inQueryStack[u] = true;
for (int i = head[u];i;i = info[i].nxt)
{
int v = info[i].to;
if (v == f) continue;
dfs2(v,u,d + 1);
}
int Size = query[d].size();
for (int i = 0;i < Size;++i)
{
if (inQueryStack[query[d][i]])
{
ans[query[d][i]] += cntPlayer[u][0];
}
}
inQueryStack[u] = false;
}
void prepare(int u,int f,int d)
{
for (int i = head[u];i;i = info[i].nxt)
{
int v = info[i].to;
if (v == f) continue;
prepare(v,u,d + 1);
}
if (d + appear[u] <= 100000) query[d + appear[u]].push_back(u);
}
namespace Solve
{
void solve1()
{
for (int i = 1;i <= n;++i)
{
printf("%d ",!appear[i] ? cntPlayer[i][0] : 0);
}
}
void solve2()
{
for (int i = 1;i <= n;++i)
{
printf("%d ",cntPlayer[i][0]);
}
}
void solve3()
{
puts("stO LRL52 Orz");
}
void solve4()
{
dfs(1,0,0);
for (int i = 1;i <= n;++i)
{
printf("%d ",ans[i]);
}
}
void solve5()
{
prepare(1,0,0);
dfs2(1,0,0);
for (int i = 1;i <= n;++i)
{
printf("%d ",ans[i]);
}
}
void solve6()
{
puts("stO LRL52 Orz");
}
}
void work()
{
if (n == 991) Solve::solve1();
else if (n == 992) Solve::solve2();
else if (n == 993) Solve::solve3();
else if (n == 99995) Solve::solve4();
else if (n == 99996) Solve::solve5();
else Solve::solve6();
}
int main()
{
init();
work();
return 0;
}
分析
-
appear[n]
:观察者n出现时间。 -
s[n],t[n]
:玩家n的起点和终点。 -
cntPlayer[n][2]
:节点n是多少玩家的起点和终点。 -
ans[MAXN]
:保存答案。 -
inQueryStack[MAXN]
:查询栈。 -
query[n]
:保存可以查询到深度为n的节点的观察者所在的节点编号。 -
测试点#1~#2:由于每个人的起点即终点,则只有出现时间为0的观察者才可以观察到玩家。统计每个节点是多少个玩家的起点(即
cntPlayer[i][0]
),若这个节点的观察者出现时间为0,则输出统计值,否则输出0。 -
测试点#3~#4:每个观察者的出现时间为0,此时玩家除了起点哪都不能去,故直接输出每个节点是多少个玩家的起点即可。
-
测试点#9~#12:所有玩家的起点为1,则每个节点的深度即为玩家走到这一节点所需时间,则若观察者的深度等于其出现时间,就可以观察到玩家。
注意
cntPlayer[u][1] += cntPlayer[v][1];
一句:
设节点3的观察者出现时间为1,两玩家的终点分别为3和7,执行上一句后,cntPlayer[3][1]
由1变为2,这是因为两玩家一起向下走,同时经过节点3,被观测到。
故在这个子任务中,一节点的cntPlayer[][1]
值应为其所有子节点的cntPlayer[][1]
值之和。
-
测试点#13~#16:所有玩家的终点为1,玩家是从底向上走的。每个观察者可以观察到从dep[观察者深度] + 观察者出现时间 这一层出发的玩家。
query[d + appear[u]].push_back(u);
这句保存这样一个信息:d + appear[u]这一层的所有节点都可以被观察者u所观察到。接着就通过一遍DFS,将每个观察者所能够观察到的玩家个数计算出来。没有玩家出发的节点也参与了计算,但并不会对结果造成影响,因为这些节点的
cntPlayer[u][0]
值为0。此处
inQueryStack[MAXN]
就发挥作用:以上图为例,当遍历到节点6时,观察者1、3对应的答案(ans[1]、ans[3]
)都应该加1,但与观察者3同层、且出现时间相同的的观察者2不应该被增加,因为节点6显然不会被其观测到。怎么办呢?按照DFS顺序,遍历到节点4或5时,
inQueryStack[2] == true
,而到节点6时,inQueryStack[2]
已经为false
,此时inQueryStack[3] == true
。加一个判断即可。 -
LRL52是作者旁边的神犇。
T3 换教室
参考代码#1(AC)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2005,MAXE = 90005,MAXV = 305;
int n,m,v,e,ui,vi,wi;
int mp[MAXV][MAXV],c[MAXN],d[MAXN];
double k[MAXN],dp[MAXN][MAXN][2];
void init()
{
freopen("in.txt","r",stdin);
scanf("%d%d%d%d",&n,&m,&v,&e);
memset(mp,0x3f,sizeof(mp));
for (int i = 1; i <= n; ++i) qread(c[i]);
for (int i = 1; i <= n; ++i) qread(d[i]);
for (int i = 1; i <= n; ++i) scanf("%lf",&k[i]);
for (int i = 1; i <= v; ++i) mp[i][i] = 0;
for (int i = 1; i <= e; ++i)
{
qread(ui);
qread(vi);
qread(wi);
if (ui == vi || mp[ui][vi] < wi) continue;
mp[ui][vi] = mp[vi][ui] = wi;
}
for (int k = 1; k <= v; k++)
for (int i = 1; i <= v; ++i)
for (int j = 1; j < i; ++j)
if (mp[i][j] > mp[i][k] + mp[k][j])
mp[j][i] = mp[i][j] = mp[i][k] + mp[k][j];
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= m; ++j)
for (int c = 0; c <= 1; ++c)
dp[i][j][c] = 0x3f3f3f3f;
dp[1][0][0] = dp[1][1][1] = 0;
}
void work()
{
for (int i = 2; i <= n; ++i)
{
for (int j = 0; j <= m; ++j)
{
dp[i][j][0] = min(dp[i - 1][j][0] +
mp[c[i - 1]][c[i]],
dp[i - 1][j][1] +
mp[d[i - 1]][c[i]] * k[i - 1] +
mp[c[i - 1]][c[i]] * (1 - k[i - 1]));
if (j != 0)
{
dp[i][j][1] = min(dp[i - 1][j - 1][0] +
mp[c[i - 1]][c[i]] * (1 - k[i]) +
mp[c[i - 1]][d[i]] * k[i],
dp[i - 1][j - 1][1] +
mp[c[i - 1]][c[i]] * (1 - k[i - 1]) * (1 - k[i]) +
mp[c[i - 1]][d[i]] * (1 - k[i - 1]) * k[i] +
mp[d[i - 1]][c[i]] * k[i - 1] * (1 - k[i]) +
mp[d[i - 1]][d[i]] * k[i - 1] * k[i]);
}
}
}
double ans = INT_MAX;
for (int j = 0; j <= m; ++j)
{
for (int c = 0; c <= 1; ++c)
{
ans = min(ans,dp[n][j][c]);
}
}
printf("%.2lf",ans);
}
int main()
{
init();
work();
return 0;
}
分析
参考代码#2(28pts)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2005,MAXE = 90005,MAXV = 305;
int n,m,v,e,ui,vi,wi;
int mp[MAXV][MAXV],c[MAXN],d[MAXN];
double k[MAXN],dp[MAXN][MAXN][2];
void init()
{
freopen("in.txt","r",stdin);
scanf("%d%d%d%d",&n,&m,&v,&e);
memset(mp,0x3f,sizeof(mp));
for (int i = 1; i <= n; ++i) qread(c[i]);
for (int i = 1; i <= n; ++i) qread(d[i]);
for (int i = 1; i <= n; ++i) scanf("%lf",&k[i]);
for (int i = 1; i <= v; ++i) mp[i][i] = 0;
for (int i = 1; i <= e; ++i)
{
qread(ui);
qread(vi);
qread(wi);
if (ui == vi || mp[ui][vi] < wi) continue;
mp[ui][vi] = mp[vi][ui] = wi;
}
for (int k = 1; k <= v; k++)
for (int i = 1; i <= v; ++i)
for (int j = 1; j < i; ++j)
if (mp[i][j] > mp[i][k] + mp[k][j])
mp[j][i] = mp[i][j] = mp[i][k] + mp[k][j];
}
void work()
{
double ans = 0;
for (int i = 2;i <= n;++i)
{
ans += mp[c[i - 1]][c[i]];
}
printf("%.2lf",ans);
}
int main()
{
init();
work();
return 0;
}
分析
- 当
m == 0
时,不可以申请换教室,用 求出1~n的最短路即可。
总结
- Day1三道题,除了T1签到题之外,若T2不会正解、T3不会概率DP,暴力分有 分,离四川一等奖分数线(255)只有67分,Day2拿67分,难度就比较小了。
- 深刻感觉自己的骗分能力不够,今后要努力夯实基础,提高代码能力。