[IOI2011]Race
本文是作者很久以前写的,仅作纪念,不建议学习做法,不保证做法正确
毕竟是人生第一道IOI(现在已经挂了又A了~~),就认真写一写吧。
竟然有爆栈这种操作(亏我花了一个多小时优化时间以为RE是TLE)
题面一看就是淀粉质。(感觉好裸)
以下是同学的挂掉做法
#include<bits/stdc++.h>
using namespace std;
template<class T>
inline void read(T &a)
{
T s = 0, w = 1;
char c = getchar();
while(c < '0' || c > '9')
{
if(c == '-') w = -1;
c = getchar();
}
while(c >= '0' && c <= '9')
{
s = (s << 1) + (s << 3) + (c ^ 48);
c = getchar();
}
a = s*w;
}
struct edge{
int from,to,cost,net;
edge(int f = 0, int t = 0, int c = 0, int n = 0)
{
from = f;
to = t;
cost = c;
net = n;
}
}edges[101010];
int head[101010],tot,k,n;
void add(int x, int y, int c)
{
edges[++tot] = edge(x,y,c,head[x]);
head[x] = tot;
}
int root = 0;
int vis[101010],mx[10101],size[101010],S;
void find(int x, int fa)
{
size[x] = 1;mx[x] = 0;
for (int i = head[x];i;i = edges[i].net)
{
edge v = edges[i];
if(vis[v.to] || v.to == fa) continue;
find(v.to,x);
size[x] += size[v.to];
mx[x] = max(mx[x],size[v.to]);
}
mx[x] = max(mx[x],S-size[x]);
if(mx[x] < mx[root])
{
root = x;
}
}
int cnt,dis[10101],deep[101010];
int minn = 0x3f3f3f3f;
int ans = 0;
void get_dis(int x,int fa,int len,int k)
{
dis[++cnt] = len;
deep[cnt] = k;
for (int i = head[x];i;i = edges[i].net)
{
edge v = edges[i];
if(vis[v.to]||v.to == fa) continue;
get_dis(v.to,x,len+v.cost,k+1);
}
}
void solve(int x,int len, int pp)
{
cnt = 0;
get_dis(x,0,len,0);
sort(dis+1,dis+1+cnt);
int l = 1,r = cnt;
while(l <= r)
{
if(dis[l] + dis[r] == k)
{
ans += pp;
if(deep[l] + deep[r] < minn)
{
minn = deep[l] + deep[r];
}
break;
}
if(dis[l] + dis[r] < k) l++;
if(dis[l] + dis[r] > k) r--;
}
/*for (int i = 1; i <= cnt; i++)
{
for (int j = 1; j <= cnt; j++)
{
if(dis[i] + dis[j] == k) ans += pp;
}
}*/
}
void Divide(int x)
{
solve(x,0,1);
vis[x] = 1;
for (int i = head[x];i;i = edges[i].net)
{
edge v = edges[i];
if(vis[v.to]) continue;
solve(v.to,v.cost,-1);
S = size[v.to]; root = 0;
find(v.to,x);
// printf("%d\n",root);
Divide(root);
}
}
int main()
{
read(n); read(k);
for (int i = 1; i < n; i++)
{
int x,y,z;
read(x); read(y); read(z);
x++;y++;
add(x,y,z);
add(y,x,z);
}
root = 0;
mx[root] = n+1;
S = n;
find(1,0);
//printf("%d\n",root);
Divide(root);
printf("%d\n",ans == 0? -1 : minn);
return 0;
}
该算法挂掉的主要原因在于使用计数器记录最小值时,一个元素无法处理错误路径这种骚操作。
于是蒟蒻的我想到了开一个大数组来计数(这TM不就是桶排吗)
于是我就自信地打上了如下代码
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<ctime>
#define ll long long
using namespace std;
inline ll read()
{
ll kkk=0;
int x=1;
char c=getchar();
while(c<'0' || c>'9')
{
if(c=='-')
x=-1;
c=getchar();
}
while(c>='0' && c<='9')
kkk=(kkk<<3)+(kkk<<1)+(c-'0'),c=getchar();
return kkk*x;
}
struct sb
{
int to,l,nextn;
}a[200001];
int n,k,num,maxn,root,z,tot,head[200001],size[200001],maxsize[200001],ans[200001],dis[200001][2];
char pd[200001];
inline int ADD(int from,int to,int l)
{
++tot;
a[tot].to=to,a[tot].l=l,a[tot].nextn=head[from],head[from]=tot;
}
inline int find(int u,int fa)
{
size[u]=1,maxsize[u]=0;
for(register int i=head[u];i!=0;i=a[i].nextn)
{
int v=a[i].to;
if(v==fa || pd[v]==1)
continue;
find(v,u);
size[u]+=size[v],maxsize[u]=max(maxsize[u],size[v]);
}
maxsize[u]=max(maxsize[u],z-size[u]);
if(maxsize[u]<maxn)
root=u,maxn=maxsize[u];
}
inline int run(int u,int fa,int l,int deep)
{
dis[++num][0]=l,dis[num][1]=deep;
for(register int i=head[u];i!=0;i=a[i].nextn)
{
int v=a[i].to;
if(v==fa || pd[v]==1)
continue;
run(v,u,l+a[i].l,deep+1);
}
}
inline int fun(int Root)
{
pd[Root]=1;
num=0;
run(Root,-1,0,0);
for(register int i=1;i<=num;++i)
for(register int j=i;j<=num;++j)
if(dis[i][0]+dis[j][0]==k)
++ans[dis[i][1]+dis[j][1]];
for(register int i=head[Root];i!=0;i=a[i].nextn)
{
int v=a[i].to;
if(pd[v]==1)
continue;
num=0;
run(v,Root,a[i].l,1);
for(register int j=1;j<=num;++j)
for(register int w=j;w<=num;++w)
if(dis[j][0]+dis[w][0]==k)
--ans[dis[j][1]+dis[w][1]];
maxn=0x7f7f7f7f,root=-1,z=size[v];
find(v,Root);
fun(root);
}
}
int main()
{
n=read(),k=read();
for(register int i=1;i<n;++i)
{
int u=read(),v=read(),k=read();
ADD(u,v,k),ADD(v,u,k);
}
maxn=0x7f7f7f7f,root=-1,z=n;
find(0,-1);
fun(root);
for(register int i=0;i<=n;++i)
if(ans[i]!=0)
{
printf("%d\n",i);
return 0;
}
putchar('-');
putchar('1');
putchar('\n');
return 0;
}
和之前那份代码明显码风不同吧
结果我尴尬地只拿了10分,蒟蒻的我开始沉思————这么高级的方法怎么会挂呢???(WTF,除了点分治高级外就全是枚举好吧)
其实我只是想检查算法正确性玩玩而已(啊啊,别打)
以上代码挂掉的主要原因是计数那一段太慢了,没错就是下面这一段
num=0;
run(Root,-1,0,0);
for(register int i=1;i<=num;++i)
for(register int j=i;j<=num;++j)
if(dis[i][0]+dis[j][0]==k)
++ans[dis[i][1]+dis[j][1]];
于是我排了个序,用用两个指针找了下合法方案,并用last记录没次二号指针的最大位置+1就过了
PS:注意路径长度之和完全可以相等,所以找到合法方案后要向下循环求解应该会被卡成跟之前那段一样的时间(离散化可以优化)
num=0;
run(Root,-1,0,0);
sort(dis+1,dis+num+1,cmp); //为了排序,我把之前的dis[][0]和dis[][1]放在了struct里面
int last=1;
for(;last<=num & dis[1].l+dis[last].l<=k;++last); //这里有个分号!!!
for(register int i=1;i<=num & dis[i].l*2<k;++i)
{
int j=last-1;
while(j>=i)
{
int P=dis[i].l+dis[j].l;
if(P<=k)
{
last=j+1;
if(P==k)
{
++ans[dis[i].deep+dis[j].deep];
while(dis[j-1].l==dis[j].l & j>i)
--j,++ans[dis[i].deep+dis[j].deep];
}
break;
}
--j;
}
}
不过我觉得last的初始化太暴力,就用二分查找小优化了以下,那都是后话了
好吧接下来是AC代码(至少以前是的现在也是)
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<ctime>
#define ll long long
using namespace std;
inline int read()
{
int kkk=0;
int x=1;
char c=getchar();
while(c<'0' || c>'9')
{
if(c=='-')
x=-1;
c=getchar();
}
while(c>='0' && c<='9')
kkk=(kkk<<3)+(kkk<<1)+(c-'0'),c=getchar();
return kkk*x;
}
struct sb
{
int to,l,nextn;
}a[400001];
int n,k,num,maxn,root,z,tot,head[400001],size[400001],maxsize[400001],ans[400001];
struct tmd
{
int l,deep;
}dis[400001];
char pd[400001];
inline int cmp(tmd x,tmd y)
{
return x.l<y.l;
}
inline int ADD(int from,int to,int l)
{
++tot;
a[tot].to=to,a[tot].l=l,a[tot].nextn=head[from],head[from]=tot;
}
inline int find(int u,int fa)
{
size[u]=1,maxsize[u]=0;
for(register int i=head[u];i;i=a[i].nextn)
{
int v=a[i].to;
if(v==fa | pd[v])
continue;
find(v,u);
size[u]+=size[v],maxsize[u]=(maxsize[u]>size[v]?maxsize[u]:size[v]);
}
maxsize[u]=(maxsize[u]>z-size[u]?maxsize[u]:z-size[u]);
if(maxsize[u]<maxn)
root=u,maxn=maxsize[u];
}
inline int run(int u,int fa,int l,int deep)
{
dis[++num].l=l,dis[num].deep=deep;
for(register int i=head[u];i;i=a[i].nextn)
{
int v=a[i].to;
if(v==fa | pd[v])
continue;
run(v,u,l+a[i].l,deep+1);
}
}
inline int fun(int Root)
{
pd[Root]=1,num=0,run(Root,-1,0,0),sort(dis+1,dis+num+1,cmp);
int last,l=1,r=num;
while(l<r)
{
int mid=(l+r)/2;
if(dis[1].l+dis[mid].l<=k)
l=mid+1;
else
r=mid;
}
last=l;
if(last==num & dis[1].l+dis[last].l<=k)
++last;
for(register int i=1;i<=num & dis[i].l*2<k;++i)
{
int j=last-1;
while(j>=i)
{
int P=dis[i].l+dis[j].l;
if(P<=k)
{
last=j+1;
if(P==k)
{
++ans[dis[i].deep+dis[j].deep];
while(dis[j-1].l==dis[j].l & j>i)
--j,++ans[dis[i].deep+dis[j].deep];
}
break;
}
--j;
}
}
for(register int i=head[Root];i;i=a[i].nextn)
{
int v=a[i].to;
if(pd[v])
continue;
num=0,run(v,Root,a[i].l,1),sort(dis+1,dis+num+1,cmp);
l=1,r=num;
while(l<r)
{
int mid=(l+r)/2;
if(dis[1].l+dis[mid].l<=k)
l=mid+1;
else
r=mid;
}
last=l;
if(last==num & dis[1].l+dis[last].l<=k)
++last;
for(register int j=1;j<=num & dis[j].l*2<k;++j)
{
int w=last-1;
while(w>=j)
{
int P=dis[j].l+dis[w].l;
if(P<=k)
{
last=w+1;
if(P==k)
{
--ans[dis[j].deep+dis[w].deep];
while(dis[w-1].l==dis[w].l & w>j)
--w,--ans[dis[j].deep+dis[w].deep];
}
break;
}
--w;
}
}
maxn=0x7f7f7f7f,root=-1,z=size[v],find(v,Root),fun(root);
}
}
int main()
{
n=read(),k=read();
for(register int i=1;i<n;++i)
{
int u=read(),v=read(),k=read();
ADD(u,v,k),ADD(v,u,k);
}
maxn=0x7f7f7f7f,root=-1,z=n;
find(0,-1),fun(root);
for(register int i=0;i<=n;++i)
if(ans[i])
{
printf("%d\n",i);
return 0;
}
putchar('-');
putchar('1');
putchar('\n');
return 0;
}
后记:
洛谷评测姬更新以后空间好像变少了,所以我的栈空间被卡爆了,大数据连找重心都跑不过。所以还需要打手动膜模拟栈来优化,但太麻烦了。(2018.8)
我今天好像发现把能换int类型的函数换成void就能过了。(2018.10.30)

浙公网安备 33010602011771号