A 追逐游戏 (chase)
后悔没做玮给的那道题了。。
就是分讨,有两种情况。
第一种是那个人先到终点,追的人追不上,这样答案就是追的人到终点的距离,在终点追上。
第二种是追的人先被追的人的毕经之路上(当然也可以一开始就在必经之路上),这样等效于一条从 \(x->s\) 的树链,被追的人走 \(\frac{x+s+1}{2}\) 步。
那这个就是类似于树上走 \(k\) 步的问题了,和上面那道题完全一样。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,q;
const int N=1e6+5;
vector<int>e[N];
int dep[N],siz[N],son[N],id[N],fa[N],rk[N];
void dfs1(int u,int f)
{
// cout<<u<<" ";
fa[u]=f,dep[u]=dep[f]+1,siz[u]=1;
for(int v:e[u])
{
if(v==f) continue;
dfs1(v,u), siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
int tim,top[N];
void dfs2(int u,int t)
{
top[u]=t,id[u]=++tim,rk[tim]=u;
if(son[u]) dfs2(son[u],t);
for(int v:e[u])
{
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
int lca(int u,int v)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
return u;
}
inline int dis(int u,int v)
{
return dep[u]+dep[v]-2*dep[lca(u,v)];
}
bool tag[N];
int jmp(int x,int k)
{
while(114)
{
if(dep[x]-dep[top[x]]+1>k) break;
k-=dep[x]-dep[top[x]]+1;
x=fa[top[x]];
}return rk[id[x]-k];
}
int s;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>s>>q;
for(int i=1;i<n;i++)
{
int u,v;cin>>u>>v;
e[u].push_back(v),e[v].push_back(u);
}
dfs1(1,0),dfs2(1,1);
while(q--)
{
int d,x;
cin>>x>>d;
if(dis(s,x)<d)
{
s=x;
cout<<s<<" ";continue;
}
int lc=lca(x,s);
int dis1=dis(s,lc),dis2=dis(x,lc);
if(dis1>d) s=jmp(s,d);
else s=jmp(x,dis2-(d-dis1));
cout<<s<<" ";
}
}
B 统计
这个题很神。考虑一个区间是符合要求的,可以简单地想到区间内元素的和是 \(\displaystyle\sum_{i=1}^m i\) 的倍数。
但是由于哈希冲突的类似问题,会有些许非法序列也符合这样的条件,那解决方法很简单。
考虑像 \(Treap\) 一样引入一个东西叫做 \(val\),对于值域内的所有数,我们给他 \(rand\) 一个 \(val\) 作为他的映射。
这样就能有效避免非法多算的情况。
我们得出的复杂度是 \(O(n^2)\),有 \(40\) 分。
进一步地,我们进行参照的和是 \(\displaystyle\sum_{i=1}^m val_i\),每次对于一个序列的前缀和去判断是否能被它整除,也就是:
\[ans=\sum_{i=1}^n \sum_{j=i+1}^{n} [(S_j-S_i) \mod \sum_{k=1}^m val_k==0] \]如果我们能让 \(\displaystyle\sum_{k=1}^mm val_k=0\),那我们的判断条件就成了 \((S_j-S_i)=0\) 也就是 \(S_j=S_i\) 了。
这样只需要统计出有多少个前缀和相等即可,每一个相等的前缀和的功效都是 \(C_n^2\) 的。
那么最简单的方法,我们令 \(\displaystyle val_m=- \sum_{i=1}^{m-1}\) 即可解决。
时间复杂度 \(O(n\log n)\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rnd rand
int m,T,n;
const int N=1e6+6;
int a[N];
long long val[N];
long long sum[N];
signed main()
{
freopen("st.in","r",stdin);
freopen("st.out","w",stdout);
mt19937 rnd(time(0));
// srand(time(NULL));
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--)
{
cin>>n>>m;
int ans=0;
for(int i=1;i<=n;i++) cin>>a[i];
int s=0;
for(int i=1;i<m;i++) val[i]=rand(),s+=val[i];
val[m]=-s;
for(int i=1;i<=n;i++) sum[i]=(sum[i-1]+val[a[i]]);
sort(sum,sum+1+n);
int cnt=1;
for(int i=1;i<=n;i++)
{
if(sum[i]!=sum[i-1]) cnt=0;ans+=cnt;cnt++;
}
cout<<ans<<"\n";
}
return 0;
}
C 软件工程
额,贪心即可。
考虑每个线段的分布很离♂ 散,那么我们就可以贪心地保留尽可能多的单个线段,因为单个线段的交是它本身。
并且,两个线段的交一定小于等于其中后任意一个线段。
那么,我们找前 \(k-1\) 个最大的线段,让它们单独成一个集合,剩下一个集合把其他线段扔进去,能拿到近乎满分。
若要满分,第一个点跑个暴力深搜就好了。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 5005
#define M 14
using namespace std;
int n,k;
struct Seg{
int l,r;
bool operator<(const Seg &x)const{
return r-l+1>x.r-x.l+1;
}
}Sg[N];
int ans;
const int inf=1e18;
int p[N];
vector<int>t[N];
int x[N],y[N];
void dfs(int stp)
{
if(stp==n+1)
{
int res=0;
for(int i=1;i<=k;i++)t[i].clear();
for(int i=1;i<=n;i++) t[p[i]].push_back(i);
for(int i=1;i<=k;i++)
{
if(!t[i].size())continue;
int tmp=t[i][0];
int jx=x[tmp],jy=y[tmp];
for(int j:t[i])
{
if(j==t[i][0]) continue;
if(jx<x[j])
{
if(jy<x[j])
{
jx=jy=0;break;
}
else jx=x[j],jy=min(jy,y[j]);
}
else
{
if(jx>y[j])
{
jx=jy=0;break;
}
else jy=min(jy,y[j]);
}
}
res+=max(0ll,jy-jx);
}
ans=max(ans,res);
return ;
}
for(int i=1;i<=k;i++)
{
p[stp]=i;
dfs(stp+1);
}
}
signed main()
{
freopen("se.in","r",stdin);
freopen("se.out","w",stdout);
scanf("%lld%lld",&n,&k);
if(n<=10)
{
for(int i=1;i<=n;i++)scanf("%lld%lld",&x[i],&y[i]);
dfs(1);
cout<<ans;
return 0;
}
for(int i=1,l,r;i<=n;i++)
{
scanf("%lld%lld",&l,&r);
Sg[i]=(Seg){l,r};
}
sort(Sg+1,Sg+1+n);
for(int i=1;i<k;i++)ans+=Sg[i].r-Sg[i].l;
int l=-inf,r=inf;
for(int i=k;i<=n;i++)
{
l=max(l,Sg[i].l);
r=min(r,Sg[i].r);
}
ans+=l>r?0:r-l;
printf("%lld",ans);
return 0;
}
D 命运的X
脑电波题,不太可想象。
最终答案是 \(m^n+\sum_{p\in nxt}m^p\),其中 \(nxt\) 是 \(B\) 数组的 Border 集合。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
int ppow(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=(res*a)%mod;
a=(a*a)%mod,b>>=1;
}return res;
}
const int N=2e5+5;
int n,m,a[N];
int nxt[N];
signed main()
{
freopen("x.in","r",stdin);
freopen("x.out","w",stdout);
int T;cin>>T;
while(T--)
{
cin>>m>>n;
for(int i=0;i<n;i++) cin>>a[i];
memset(nxt,0,sizeof(nxt));
int j=0;
nxt[1]=0;
for(int i=2;i<=n;i++)
{
while(j&&a[i]!=a[j+1]) j=nxt[j];
if(a[j+1]==a[i])j++;
nxt[i]=j;
}
int ans=ppow(m,n);
int now=nxt[n];
while(now) ans=(ans+ppow(m,now))%mod,now=nxt[now];
cout<<ans%mod<<"\n";
}
}