9.19 考试总结
作者:互联网
前言:
貌似自己的开题和别人好像不太一样,别人都是 \(T1-T2-T3\) ,好像没几个像我一样 \(T3-T2-T1\) 的。大雾
T1 潜伏
题目描述:
⼩悠回家之后,跟着⺟亲看了很多抗⽇神剧,其中不乏⼀些谍战⽚。
解放前夕,北平城内潜伏着若⼲名地下党员,他们居住在城市的不同位置。现在身为地下党第⼀指挥官
的你,想知道地下党员之间的最⼩通信距离,即从某⼀地下党员住处前往另⼀地下党员住处的距离的最
⼩值。
我们可以将北平城视为⼀张 \(N\) 个点 \(M\) 条边的⽆向图,每条边连接两个点 \(u_i,v_i\),且长度为 \(w_i\)。
输入格式:
每个测试点包含多组数据。
第⼀⾏,给出数据组数 \(T\),之后依次输⼊每组数据。
每组数据的第⼀⾏ \(N\),\(M\) , \(K\) 分别表示点数,边数,地下党员数。
之后 \(M\) ⾏,每⾏ \(u_i,v_i,w_i\) 表示第 \(i\) 条边。
之后⼀⾏,\(k\) 个整数代表地下党员所在结点。
结点编号为 \(1\) 到 \(N\),保证 \(0 \leq k \leq N\)。
输出格式:
对于每组数据,输出⼀⾏⼀个整数,表示地下党员之间的最⼩通信距离。
如果最⼩通信距离为 \(\infty\),请输出 \(-1\) 代替
说明与提示:
对于所有测试点 \(T\leq 100\)。
对于 \(50%\) 的测试点 \(N\leq 1000\),\(M\leq 2000\) 。
对于 \(20%\) 的测试点 \(N\leq 100000\),输⼊的⽆向图⽆环。
对于30%的测试点 \(N\leq 100000\),\(m\leq 200000\) 。
所有 \(0\leq w_i\leq 1000\) 。
一个比较裸的多源最短路问题,但我当时没想出来用二进制来压缩状态,直接就GG了。
只能打个 \(50pts\) 暴力滚粗。
50pts Code :
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define int long long
const int N = 1e5+10;
int T,n,m,k,u,v,w,ans,tot;
int head[N],dis[N],c[N];
bool vis[N];
struct node
{
int to,net,w;
}e[N<<1];
inline int read()
{
int s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
void add(int x,int y,int w)
{
e[++tot].to = y;
e[tot].w = w;
e[tot].net = head[x];
head[x] = tot;
}
void spfa(int x)
{
priority_queue<pair<int,int> > q;
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
q.push(make_pair(0,x)); dis[x] = 0;
while(!q.empty())
{
int t = q.top().second; q.pop();
if(vis[t]) continue;
vis[t] = 1;
for(int i = head[t]; i; i = e[i].net)
{
int to = e[i].to;
if(dis[to] > dis[t] + e[i].w)
{
dis[to] = dis[t] + e[i].w;
q.push(make_pair(-dis[to],to));
}
}
}
}
void qingkong()
{
tot = 0;
memset(head,0,sizeof(head));
memset(dis,0,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(c,0,sizeof(c));
}
signed main()
{
// freopen("hide.in","r",stdin);
// freopen("hide.out","w",stdout);
T = read();
while(T--)
{
qingkong();
n = read(); m = read(); k = read(); ans = 1e15;
for(int i = 1; i <= m; i++)
{
u = read(); v = read(); w = read();
add(u,v,w); add(v,u,w);
}
for(int i = 1; i <= k; i++) c[i] = read();
for(int i = 1; i <= k; i++)
{
spfa(c[i]);
for(int j = i+1; j <= k; j++)
{
ans = min(ans,dis[c[j]]);
}
}
if(ans == 1e15) printf("%lld\n",-1);
else printf("%lld\n",ans);
}
fclose(stdin); fclose(stdout);
return 0;
}
T2 神经
题目描述:
神经⽹络可以表示为 \(N\) 个点 \(M\) 条边的有向图,每个结点带有⼀个兴奋权值 \(w_i\)。
如果以 \(x\) 单位⼤⼩的电流刺激结点 ,设 \(v_1,v_2,.....v_k\) 是从 \(x\) 出发可以到达的结点,
则神经⽹络会产⽣ \(x\times max(w_{v_1},w_{v_2}.....w_{v_k})\)的兴奋度,请注意,我们认为从 \(u\) 出发可以到达 \(u\) 。
现在请你回答若⼲询问,每个询问表示为:在以 \(x\) ⼤⼩的电流刺激 \(u\) 点后,⽹络的兴奋度是多少。
输入格式:
每个测试点包含多组数据。
第⼀⾏,⼀个整数 \(T\) 表示数据组数,之后依次输⼊每组数据。
每组数据第⼀⾏, \(N,M,K\) 分别表示点数,边数,询问次数。
之后⼀⾏, \(N\) 个整数, \(w_1,w_2,.....w_N\)表示每个点的兴奋权值。
之后 \(M\) ⾏,每⾏ \(u_iv_i\) 表示⼀条从 \(u_i\) 到 \(v_i\) 的单向边。
之后 \(k\) ⾏,每⾏ \(u,x\) 表示⼀次刺激。
输出格式:
对于每个测试点中的每个询问,输出⼀⾏⼀个整数表示答案。
数据范围与提示:
对于所有数据, \(T\leq 5\)
对于50%的测试点, \(N\leq100,M\leq 2000,K\leq 1000\)。
对于20%的测试点, \(N\leq 100000,M\leq 200000,K\leq 100000\),输⼊的有向图构成DAG。
对于30%的测试点 \(N\leq 200000,M\leq 400000,K\leq 100000\)。
所有 \(0\leq x,w_i\leq 10^9\)。
这道题在数据范围的时候已经给出做法了,输入保证有向图构成 \(DAG\) 直接对整张图跑一边 拓扑排序加 \(dp\) 就可以解决。
但数据没有保证不存在环,所以我们需要对整张图缩一下点,然后对每个强连通分量再跑拓扑排序。
另外一种写法就是对整张图跑 \(dij\) 进行松弛操作,那种写法实现起来要比这个好写点。
Code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define int long long
const int N = 2e5+10;
int T,n,m,k,u,o,x,num,top,cnt,tot;
int dfn[N],low[N],head[N],shu[N],w[N],f[N],du[N],sta[N];
bool vis[N];
vector<int> v[N];
inline int read()
{
int s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
struct node
{
int to,net;
}e[N<<1];
void add(int x,int y)
{
e[++tot].to = y;
e[tot].net = head[x];
head[x] = tot;
}
void tarjain(int x)//缩点
{
dfn[x] = low[x] = ++num;
sta[++top] = x; vis[x] = 1;
for(int i = head[x]; i; i = e[i].net)
{
int to = e[i].to;
if(!dfn[to])
{
tarjain(to);
low[x] = min(low[x],low[to]);
}
else if(vis[to])
{
low[x] = min(low[x],dfn[to]);
}
}
if(dfn[x] == low[x])
{
cnt++; int y;
do
{
y = sta[top--];
shu[y] = cnt;
f[cnt] = max(f[cnt],w[y]);
vis[y] = 0;
}while(x != y);
}
}
void rebuild()
{
for(int i = 1; i <= n; i++)
{
for(int j = head[i]; j; j = e[j].net)
{
int to = e[j].to;
if(shu[i] != shu[to])
{
v[shu[to]].push_back(shu[i]);
du[shu[i]]++;
}
}
}
}
void tupo()//拓扑排序加dp
{
queue<int> q;
for(int i = 1; i <= cnt; i++)
{
if(du[i] == 0) q.push(i);
}
while(!q.empty())
{
int t = q.front(); q.pop();
for(int j = 0; j < (int) v[t].size(); j++)
{
int to = v[t][j];
// cout<<to<<" "<<t<<endl;
f[to] = max(f[to],f[t]);
if(--du[to] == 0)
{
q.push(to);
}
}
}
}
void qingkong()
{
tot = num = cnt = top = 0;
memset(head,0,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(f,0,sizeof(f));
memset(vis,0,sizeof(vis));
memset(du,0,sizeof(du));
memset(w,0,sizeof(w));
memset(shu,0,sizeof(shu));
for(int i = 1; i <= n; i++) v[i].clear();
}
signed main()
{
// freopen("neural.in","r",stdin);
// freopen("neural.out","w",stdout);
T = read();
while(T--)
{
qingkong();
n = read(); m = read(); k = read();
for(int i = 1; i <= n; i++) w[i] = read();
for(int i = 1; i <= m; i++)
{
u = read(); o = read();
add(u,o);
}
for(int i = 1; i <= n; i++) //缩点
{
if(!dfn[i]) tarjain(i);
}
rebuild(); tupo();//重新建图加拓扑排序
for(int i = 1; i <= k; i++)
{
u = read(); x = read();
printf("%lld\n",x * f[shu[u]]);
}
}
fclose(stdin); fclose(stdout);
return 0;
}
T3计树
题目描述:
小悠的导师对于树叶颜⾊有很深的研究,但是碍于眼神不好,总是要请⼩悠来帮忙数⼀数树上某种颜⾊的叶⼦有多少⽚。
给出一棵 \(N\) 个结点的树,每个结点初始具有⼀个颜⾊ ,现在有如下两种操作——
- 更改结点 \(u\) 的颜⾊为 \(x\) 。
- 询问从 \(u\) 到 \(v\) 的路径上有多少个颜⾊为 \(x\) 的结点。
现在请你按顺序完成若⼲次操作。
输入格式:
每个测试点包含多组数据。
第 \(1\),个整数 表示数据组数 \(T\),之后依次输⼊每组数据。
每组数据第 \(N\),\(M\) 分别表示点数、操作次数。
之后 \(N\) 个整数 表示每个点的初始颜⾊。
之后 \(n-1\) ,每⾏两个整数 \(u\),\(v\) ,表示树上存在⼀条连接 的边。
之后 \(m\) ,表示每个操作。
如果是操作 \(1\),则形如 \(1,u,x\)。
如果是操作 \(2\),则形如 \(1,u,v,x\)。
输出格式:
对于每组数据的每个操作2,输出⼀⾏⼀个整数表示答案。
数据范围与提示:
对于所有测试点 \(T \leq 5\)。
对于50%的测试点 \(N\leq 1000\),\(M\leq 1000\) 。
对于20%的测试点 \(N\leq 100000\),\(M\leq 100000\) ,颜⾊种类数不超过5。
对于30%的测试点 \(N\leq 100000\),\(M\leq 100000\) 。
所有测试点颜⾊种类数均不超过 \(100\) 。
一开始我想这不就是把数颜色那道题套的树上了吗?直接上树上带修莫队,然鹅自己并不会写。
看了一眼数据范围才发现,这道题是个树剖套线段树的裸题,直接对每个颜色开个线段树来维护就好了。
三十分钟码完,结果debug 用了一个小时。
为了卡一下时间,我还特意把建树过程省了。结果在 \(lemon\) 上测还是 \(TLE\) ,但在 \(oj\) 上就测得没问题,1e5的数据完全可以跑过去。
\(lemon\) 差评,大雾
Code :
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5+10;
int n,m,u,v,x,tot,num,T,opt;
int head[N],dep[N],dfn[N],siz[N],son[N],fa[N],top[N];
int tr[110][N<<2],col[N];
struct node
{
int to,net;
}e[N<<1];
inline int read()
{
int s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
void add(int x,int y)
{
e[++tot].to = y;
e[tot].net = head[x];
head[x] = tot;
}
void get_tree(int x)
{
dep[x] = dep[fa[x]] + 1; siz[x] = 1;
for(int i = head[x]; i; i = e[i].net)
{
int to = e[i].to;
if(to == fa[x]) continue;
fa[to] = x;
get_tree(to);
siz[x] += siz[to];
if(siz[to] > siz[son[x]]) son[x] = to;
}
}
void dfs(int x,int topp)
{
top[x] = topp; dfn[x] = ++num;
if(son[x]) dfs(son[x],topp);
for(int i = head[x]; i; i = e[i].net)
{
int to = e[i].to;
if(to == fa[x] || to == son[x]) continue;
dfs(to,to);
}
}
void up(int now,int o)
{
tr[now][o] = tr[now][o<<1] + tr[now][o<<1|1];
}
void chenge(int now,int o,int L,int R,int x,int val)
{
if(L == R)
{
tr[now][o] += val;
return;
}
int mid = (L + R)>>1;
if(x <= mid) chenge(now,o<<1,L,mid,x,val);
if(x > mid) chenge(now,o<<1|1,mid+1,R,x,val);
up(now,o);
}
int query(int now,int o,int L,int R,int ql,int qr)
{
int res = 0;
if(ql <= L && qr >= R) return tr[now][o];
int mid = (L + R)>>1;
if(ql <= mid) res += query(now,o<<1,L,mid,ql,qr);
if(qr > mid) res += query(now,o<<1|1,mid+1,R,ql,qr);
return res;
}
int ask(int x,int y,int now)
{
int res = 0;
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) swap(x,y);
res += query(now,1,1,n,dfn[top[x]],dfn[x]);
x = fa[top[x]];
}
if(dep[x] >= dep[y]) swap(x,y);
res += query(now,1,1,n,dfn[x],dfn[y]);
return res;
}
void qingkong()
{
tot = num = 0;
memset(col,0,sizeof(col));
memset(head,0,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(tr,0,sizeof(tr));
memset(dep,0,sizeof(dep));
memset(fa,0,sizeof(fa));
memset(siz,0,sizeof(siz));
memset(son,0,sizeof(son));
memset(top,0,sizeof(top));
}
int main()
{
// freopen("count.in","r",stdin);
// freopen("count.out","w",stdout);
T = read();
while(T--)
{
qingkong();
n = read(); m = read();
for(int i = 1; i <= n; i++) col[i] = read();
for(int i = 1; i <= n-1; i++)
{
u = read(); v = read();
add(u,v); add(v,u);
}
get_tree(1); dfs(1,1);
for(int i = 1; i <= n; i++)
{
chenge(col[i],1,1,n,dfn[i],1);
}
for(int i = 1; i <= m; i++)
{
opt = read();
if(opt == 1)
{
u = read(); x = read();
chenge(col[u],1,1,n,dfn[u],-1);
chenge(x,1,1,n,dfn[u],1);
col[u] = x;
}
else if(opt == 2)
{
u = read(); v = read(); x = read();
printf("%d\n",ask(u,v,x));
}
}
}
fclose(stdin); fclose(stdout);
return 0;
}
口胡一下正解的写法吧,我们可以对于每个颜色都开个 \(vector\) 存涉及到这个颜色的操作,也就是把颜色离线下来,之后再枚举每个颜色,统计每个询问的答案就
可以,复杂度貌似是 \(clog^2n\) 的,跑的飞快 \(300ms\) 都可以跑过去。
总结
期望得分:50+100+100 = 250
实际上因为 \(lemon\) 的一些问题,直接炸成 \(50+100+70 = 220\) ,芜湖原地起飞、
感觉这套题的思维难度不是很大,但代码难度的话却不一样了,写完了 \(debug\) 就需要好几个小时。
这难道就是所谓的 码农局,大雾
另外,每题都是多组数据,每次都要注意清空,好烦。
标签:总结,ch,测试点,9.19,int,memset,leq,include,考试 来源: https://www.cnblogs.com/genshy/p/13703569.html