noip50
T1
直接线段树维护区间和显然不对差点就码了,然而暴力还极易暴零,找到LCA后,从 \(s,t\) 开始往LCA跳就错了,想一下发现确实是这样的,40pts->0pts。
还是菜,考虑的少了。
40pts: 暴力,注意从一个点跳到LCA,再从LCA跳下来,搞错了直接暴零。
60pts: 就是多了链的分,自己模几组就能发现,对于一条链,从 \(s\) 出发,和从 \(t\) 出发,两者复活次数是一样的,但是直接写暴力跳无法过掉链的点,容易想到用倍增来解决,设 \(die_{i,j}\) 表示点 \(i\) 复活了 \(2^{j}\) 后所到达的点,那么现在问题就是求出点 \(i\) 第一次复活所到达的点,求个树上前缀和,直接暴力往上跳即可,查询就通过 \(die\) 数组找到 \(s,t\) 分别离 \(LCA(s,t)\) 最近且血是满的即恰好复活的点,然后再判断一下这两个点形成的路径上的权值和与 \(k\) 的关系即可,这样的话就是60pts,然而数据过氵,能100pts。
100pts: 把60pts里的暴力找第一次复活所到达的点改成二分即可,复杂度 \(O(n\log n)\) 。
Code
#include<cstdio>
#include<algorithm>
#define MAX 200003
#define re register
#define int64_t long long
using std::upper_bound;
namespace some
{
int n,k,q;
int bin[MAX];
struct stream
{
template<typename type>inline stream &operator >>(type &s)
{
int w=1; s=0; 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,*this;
}
}cin;
struct graph
{
int next;
int to;
int w;
}edge[MAX<<1];
int cnt=1,head[MAX];
auto add = [](int u,int v,int w) ->void { edge[++cnt] = (graph){head[u],v,w},head[u] = cnt; };
}using namespace some;
namespace TC
{
int die[MAX][21];
int64_t sum[MAX],dis[MAX];
int sta[MAX],top[MAX];
int dep[MAX],size[MAX];
int fa[MAX][21],son[MAX];
inline void dfs1(int u)
{
size[u] = 1;
sta[++top[0]] = u,dis[top[0]] = sum[u];
die[u][0] = sta[upper_bound(dis+1,dis+1+top[0],sum[u]-k)-dis-1];
for(re int i=1; i<=bin[dep[u]]; i++)
{ fa[u][i] = fa[fa[u][i-1]][i-1],die[u][i] = die[die[u][i-1]][i-1]; }
for(re int i=head[u],v/*,p*/; i; i=edge[i].next)
{
v = edge[i].to;
if(v!=fa[u][0])
{
sum[v] = sum[u]+edge[i].w;
dep[v] = dep[fa[v][0] = u]+1;
/*p = u;
while(p)
{
if(sum[v]-sum[p]>=k)
{ die[v][0] = p; break ; }
p = fa[p][0];
}*/
dfs1(v);
size[u] += size[v];
if(!son[u]||size[v]>size[son[u]])
{ son[u] = v; }
}
}
top[0]--;
}
inline void dfs2(int u,int t)
{
top[u] = t;
if(son[u])
{ dfs2(son[u],t); }
for(re int i=head[u],v; i; i=edge[i].next)
{
v = edge[i].to;
if(v!=fa[u][0]&&v!=son[u])
{ dfs2(v,v); }
}
}
auto LCA = [](int a,int b) -> int
{
while(top[a]!=top[b])
{
if(dep[top[a]]>dep[top[b]])
{ a = fa[top[a]][0]; }
else
{ b = fa[top[b]][0]; }
}
return dep[a]<dep[b]?a:b;
};
}using namespace TC;
namespace OMA
{
auto main = []() -> signed
{
//freopen("node.in","r",stdin);
//freopen("my.out","w",stdout);
cin >> n >> k; dep[1] = 1;
for(re int i=2,u,v,c; i<=n; i++)
{ cin >> u >> v >> c; add(u,v,c),add(v,u,c); bin[i] = bin[i>>1]+1; }
dfs1(1),dfs2(1,1);
//printf("QAQ\n");
/*for(re int i=1; i<=n; i++)
{
printf("dep[%d]=%d\n",i,dep[i]);
for(re int j=0; j<=bin[dep[i]]; j++)
{ printf("%d ",die[i][j]); }
printf("\n");
}*/
cin >> q;
for(re int i=1,s,t,lca,ans; i<=q; i++)
{
cin >> s >> t;
lca = LCA(s,t),ans = 0;
//printf("dep[s=%d]=%d bin1=%d dep[t=%d]=%d bin2=%d lca=%d\n",s,dep[s],bin[dep[s]],t,dep[t],bin[dep[t]],lca);
for(re int j=bin[dep[s]]; ~j; j--)
{
//printf("%d ",die[s][j]);
if(dep[die[s][j]]>=dep[lca])
{ /*printf("s: which=%d\n",j);*/ ans += 1<<j,s = die[s][j]; /*break ;*/ }
}
//printf("\n");
for(re int j=bin[dep[t]]; ~j; j--)
{
//printf("%d ",die[t][j]);
if(dep[die[t][j]]>=dep[lca])
{ /*printf("t: j=%d which=%d\n",j,die[t][j]);*/ ans += 1<<j,t = die[t][j]; /*break ;*/ }
}
//printf("\n");
if(sum[s]+sum[t]-2*sum[lca]>=k)
{ /*printf("done ");*/ ans++; }
printf("%d\n",ans);
}
return 0;
};
}
signed main()
{ return OMA::main(); }
T2
读题出锅,啥分也没有,求和搞成取 \(\max\) 的估计就我一个了。
20pts: 直接 \(n^{2}\) dp即可,注意是求和,不是取 \(\max\)。
100pts: 正解分治,先咕了。
T3
\(n^{2}\) 过百万,暴力碾标算!
考场上因为自己的某些神奇操作,导致我生成数据生成错了,今早重新粘了一下就生成对了.....
\(O(n^{2})\) 做法:
就是暴力.....
这个 \(n^2\) 跑不满,所以能过原数据。
逝去的100pts
#include<cstdio>
#define MAX 5000003
#define re register
typedef unsigned long long ull;
ull A,B;
int n,L,X,Y;
int l[MAX],r[MAX];
int lpre[MAX],rpre[MAX];
const int p = 998244353;
namespace some
{
auto swap = [](int &a,int &b) ->void { int t=a; a=b; b=t; };
auto xorshift = []()
{
ull T = A, S = B;
A = S;
T ^= T<<23,T ^= T>>17,T ^= S^(S>>26);
B = T;
return T+S;
};
auto make_data = []() -> void
{
for(int i=1; i<=n; i++)
{
l[i] = xorshift()%L+X,r[i] = xorshift()%L+Y;
if(l[i]>r[i]){ swap(l[i],r[i]); }
lpre[i] = l[i],rpre[i] = r[i];
//{ printf("%d %d\n",l[i],r[i]); }
}
};
auto max = [](int a,int b) -> int { return a>b?a:b; };
auto min = [](int a,int b) -> int { return a<b?a:b; };
auto mod = [](int a,int b) -> int { return b-p+a>=0?b-p+a:a+b; };
}using namespace some;
namespace OMA
{
int f[MAX],rec,cnt,ans;
auto update = [](int i) -> void { lpre[i] = l[i],rpre[i] = r[i]; };
auto main = []() -> signed
{
scanf("%d%d%d%d%lld%lld",&n,&L,&X,&Y,&A,&B); make_data();
f[1] = f[n] = 1,r[1] = r[n] = 0;
while(++rec)
{
for(re int i=2; i<=n-1; i++)
{
if(!r[i])
{ /*printf("1: rec=%d %d\n",rec,i);*/ update(i-1); continue ; }
if(!rpre[i-1]||!rpre[i+1])
{ /*printf("2: rec=%d %d\n",rec,i);*/ update(i-1),r[i] = 0,f[i] = rec; continue ; }
l[i] = max(l[i]+1,max(lpre[i-1],lpre[i+1]));
r[i] = min(r[i]-1,min(rpre[i-1],rpre[i+1]));
//printf("id=%d l=%d r=%d\n",i,l[i],r[i]);
if(l[i]>r[i])
{ r[i] = 0,f[i] = rec; }
//printf("3: rec=%d %d\n",rec,i);
update(i-1);
}
update(n-1),update(n),cnt = 0;
for(re int i=1; i<=n; i++)
{ cnt += !r[i]; }
if(cnt==n)
{ break ; }
}
for(re int i=1,j=1; i<=n; i++,j=1LL*j*3%p)
{ /*printf("f[%d]=%d\n",i,f[i]);*/ ans = mod(ans,1LL*f[i]*j%p); }
printf("%d\n",ans);
return 0;
};
}
signed main()
{ return OMA::main(); }
题解做法:
并不会
upd on 09-10 night
下午刚氵过,晚上就被战神卡了QAQ,研究正解去了,可能会填上.....