挂大分啦!!呜呜呜
赛时
开考先开 T 1 T1 T1,发现好像会打 25 p t s 25pts 25pts的暴力分数,然而当时没有好好想就开打了,这么多条性质竟然一条性质都没有发现,直接敲了一个 L C A + R M Q + D i j LCA+RMQ+Dij LCA+RMQ+Dij的 150 150 150行超长代码(真的诠释了什么叫暴力比正解难写(╯﹏╰)(╯﹏╰) ,预估 25 p t s 25pts 25pts
然后开 T 2 T2 T2,没有任何头绪再次打起了暴力,不知道怎么了,暴力打的稀碎,调了很久才过了样例,预估 20 p t s 20pts 20pts
T 3 T3 T3一开始连题目都读不懂,后来就根据题意硬模拟,(硬到什么程度呢,就是说出现在循环里套 s o r t sort sort的行为),但还好出题人给了小数据,预估$30pt4s
T 4 T4 T4一眼 f l o y d floyd floyd,转念一想这是 T 4 T4 T4啊,再好好看了一下题,发现有去重问题,没时间写了
赛后
心态炸了,T2T飞了,挂了20分,总分55分,排名倒数┭┮﹏┭┮呜呜呜
第一次尝试写一下题解,不知道写的好不好
T1
题目简介:大小为n个点的树上有m条路径,路径的起点和终点都可以到达这条路径上的所有点,从点1出发,只能走给定的路径,求经过的最小路径数量
25pts做法,把路径起点和终点与路径上的所有点连线,跑单源最短路超级复杂的
L
C
A
+
R
M
Q
+
D
i
j
LCA+RMQ+Dij
LCA+RMQ+Dij做法,只有我这个大冤种写了吧,放一下考场的代码吧`
#include <bits/stdc++.h>
#define ll long long
#define F(i,a,b) for(int i = (a);i<=(b);i++)
#define R register
using namespace std;
inline int read() {R int x=0,t=1;R char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') t=-1;ch=getchar();}while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*t;}
const int N=1e5+10;
int n,m,p[N],f[N][40],dep[N],id[N],tot,Fa[N],dis[N];
vector<int>G[N],g[N];
inline void ADD(int x,int y)
{
G[x].push_back(y);
return;//树边
}
inline void add(int x,int y)
{
g[x].push_back(y);//新边(巴士)
return;
}
void dfs(int u,int fa)
{
Fa[u]=fa;
dep[u]=dep[fa]+1;
f[++tot][0]=u;
id[u]=tot;
for(int i = 0;i<G[u].size();i++){
int v=G[u][i];
if(v!=fa){
dfs(v,u);
f[++tot][0]=u;
}
}
return;
}
void find(int u,int q,int LCA)
{
if(u!=q)
{
// cout << q << "-->>" << u << '\n';
add(q,u);//建边
}
if(u==LCA)
{
return; //到头了不能再上了
}
find(Fa[u],q,LCA);
return;
}
void find1(int u,int q,int LCA)
{
if(u!=q && u!=LCA)//LCA再find里面加过了
{
// puts("114514");
// cout << q << "-->>" << u << '\n';
add(q,u);//建边
}
if(u==LCA)
{
return; //到头了不能再上了
}
find1(Fa[u],q,LCA);
return;
}
int cmp(int x,int y)
{
return dep[x]<dep[y] ? x : y;//取深度更浅的
}
inline int lca(int u,int v)
{
if(id[u]>=id[v]) swap(u,v);
int k=log2(id[v]-id[u]+1);
return cmp(f[id[u]][k],f[id[v]-(1<<k)+1][k]);
}
struct Node
{
int id,dis;
};
bool operator <(const Node &x,const Node &y)
{
return x.dis<y.dis;
}
void dij()
{
priority_queue<Node>q;
dis[1]=0;
memset(dis,0x3f,sizeof dis);
dis[1]=0;
q.push({1,0});
//cout << dis[1] << '\n';
while(q.size())
{
int u=q.top().id;
q.pop();
for(int i = 0;i<g[u].size();i++){
int v=g[u][i];
if(dis[v]>dis[u]+1){
dis[v]=dis[u]+1;
q.push({v,dis[v]});
}
}
}
return;
}
inline void solve()
{
bool flag=0;
n=read(),m=read();
F(i,1,n-1){
p[i]=read();
if(p[i]!=i) flag=1;
ADD(i+1,p[i]);
ADD(p[i],i+1);//树边建边
}
dfs(1,-1);//遍历树,预处理LCA
for(int j = 1;(1<<j)<=tot;j++)//st表预处理LCA
{
for(int i = 1;i<=tot;i++)
{
f[i][j]=cmp(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
F(i,1,m){
int ui=read(),vi=read();
find(ui,ui,lca(ui,vi));
find1(vi,ui,lca(ui,vi));//提取路上的节点与ui连边
find(vi,vi,lca(ui,vi));
find1(ui,vi,lca(ui,vi));//提取路上的节点与vi连边
}
dij();
F(i,1,n)
{
if(dis[i]>5e6) cout << -1 << " ";
else
cout << dis[i] << ' ';
}
//printf("%d\n",n);
}
int main()
{
freopen("A.in","r",stdin);
freopen("A.out","w",stdout);
solve();
return 0;
}
100pts做法:考虑从dij下手,发现边权都是1,dij等价于bfs。思考跑bfs的过程易得,一个点被遍历到了,那么它的树上所有祖先都被遍历过了。所以遍历到一个路径的起点或终点时,遍历这条路径,发现有点被遍历过了就停下,这个过程是线性的,顶多是遍历路径时的两个端点会多次遍历,于是复杂度为
O
(
n
+
m
)
O(n+m)
O(n+m)可以通过此题。
代码:
#include <bits/stdc++.h>
#define ll long long
#define F(i,a,b) for(int i = (a);i<=(b);i++)
#define R register
using namespace std;
inline int read() {R int x=0,t=1;R char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') t=-1;ch=getchar();}while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*t;}
const int N=1e6+10;
int n,m,f[N],fa[N];
queue<int>q;
vector<int>g[N];
inline void work(int x,int v)
{
while(f[x]==-1)//尚未进栈
{
f[x]=v;
q.push(x);
x=fa[x];
}
return;
}
inline void solve()
{
n=read(),m=read();
F(i,2,n) fa[i]=read();
F(i,1,m){
int ui=read(),vi=read();
g[ui].push_back(vi);
g[vi].push_back(ui);
}
memset(f,-1,sizeof f);
f[1]=0;
q.push(1);
while(q.size())
{
int u=q.front();
q.pop();
for(int i = 0;i<g[u].size();i++)
{
int v=g[u][i];
work(v,f[u]+1);
}
}
F(i,1,n) cout << f[i] << " ";
}
int main()
{
freopen("A.in","r",stdin);
freopen("A.out","w",stdout);
solve();
return 0;
}
T2
数据强制在线,卡掉了奇奇怪怪的离线做法。可以看到,每个地点的最后的大合照其实都是固定的,都和初始在校门口的一样。正难则反,考虑从结束状态慢慢删除人,用链表维护,复杂度
O
(
n
q
)
O(nq)
O(nq),可以通过此题。
代码:
#include <bits/stdc++.h>
#define int long long
#define ll long long
#define F(i,a,b) for(int i = (a);i<=(b);i++)
#define R register
using namespace std;
inline int read() {R int x=0,t=1;R char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') t=-1;ch=getchar();}while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*t;}
const int N=5e5+10;
int n,m,h[N],a[N],p[N],now,l[N],r[N];
inline int ask(int x,int y)
{
if(x==0 || y==n+1) return 0;
return (a[x]-a[y])*(a[x]-a[y]);
}
inline int work()
{
for(int i = 1;i<=n;i++) l[i]=i-1,r[i]=i+1;
int pos=now,w=0,ans=0;
for(int i = 1;i<n;i++)
{
w+=(a[i]-a[i+1])*(a[i]-a[i+1]);//结束状态就是校门口的初始状态
}
ans+=w;
if(pos==0) pos=n;
for(int i = 1;i<=n;i++)
{
int t=p[pos];
w-=ask(l[t],t);
w-=ask(t,r[t]);
w+=ask(l[t],r[t]);
r[l[t]]=r[t];
l[r[t]]=l[t];
ans+=w;
pos--;
if(pos==0) pos=n;
}
return ans;
}
inline void solve()
{
n=read(),m=read();
F(i,1,n) a[i]=read();
F(i,1,n) p[i]=read();
int k,lst=work();
cout << lst << '\n';
F(i,1,m)
{
k=read();
now+=lst+k;
now%=n;
lst=work();
cout << lst << '\n';
}
}
signed main()
{
freopen("C.in","r",stdin);
freopen("C.out","w",stdout);
solve();
return 0;
}
T 3 T3 T3和 T 4 T4 T4在下一篇博客里写。
第一回写题解,紧张捏