结果:
A 题 等比数列二分求和 我zz 求逆 混了80 费马小定理 要求模数为质数并且互质 但是扩欧只要满足有解就行
B 题 我没打 没看......
C 题 我觉得树剖很好想 就去一直打树剖 然后打炸了 思路错 只有10 分 ....
我佛了
反思:
下来他们都说 B 题很简单 我。。。
当时没想这么多 就像 练练我树剖的技巧 但是写了将近两个小时 还写挂了 .....
A 病毒分裂
A 学校的实验室新研制出了一种十分厉害的病毒。由于这种病毒太难以人工制造了,所以专家们在一开始只做出了一个这样的病毒。
这个病毒被植入了特殊的微型芯片,使其可以具有一些可编程的特殊性能。最重要的一个性能就是,专家们可以自行设定病毒的分裂能力K,假如现在有x 个病毒,下一个分裂周期将会有Kx 个一模一样的病毒。你作为该实验室的数据分析员,需要统计出在分裂到第N 个周期前,一共有多少个病毒单体进行了分裂。一开始时总是只有一个病毒,这个局面算作第一个周期。由于答案可能很大,专家们只需要你告诉他们对给定的P 取模后的答案。
解 :
答案为 \(\sum_0^{n-2} k^i\)
我考试的时候居然没有看出来这是一个等比数列在求和 xxx 我还想到了费马小定理去降幂 ... 我还是想的太过于复杂 ...
然后 我zz 求逆 混了80 费马小定理 要求模数为质数并且互质 但是扩欧只要满足有解就行
正解为等比数列二分求和
当n 为 奇数时 将奇数项提出来
n为偶数时 直接 分治递归
我会说我没开ll 调试了一个多小时吗??
放一波模板 我的代码可能和标程不一样
//
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll k,n,p;
ll ksm(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1) ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans%p;
}
ll dfs(ll r)
{
if(r<0) return -1;
if(r==0) return 0;
if(r==1) return k;
ll s=dfs(r/2)%p;
ll s1=ksm(k,r/2)%p;
if(r&1)
{
ll s2=ksm(k,r)%p;
return (s*(1+s1)%p+s2)%p;
}
else
{
return (s*(1+s1)%p)%p;
}
}
int main()
{
cin>>k>>n>>p;
k%=p;
ll tmp=(1+dfs(n-2))%p;
cout<<tmp%p;
B 疫情延迟
由于A 学校生物实验室里那个不负责的数据分析员,实验室的病毒威力被错误估算,导致了可怕的病毒泄漏,现在病毒即将在校园内传播开来。
校园里一共有n 个建筑物,生物实验室总是位于一号建筑物且在0 时刻受到病毒入侵。这n 个建筑物由m 条单向道路相连(也有可能有建筑物被孤立)。每条道路有两个信息:它的长度,它是多少年前修建的。当一个建筑物被病毒入侵,从被入侵的时刻起,病毒会从所有由这个建筑物通向其他建筑物的道路按着这条道路的方向以1 个单位每秒的速度行进。
校长得知这个事情后,决定放弃这个学校逃跑。校长总是位于编号为n 的行政楼,从零时刻开始需要共T 秒来逃出行政楼,且逃出行政楼即视为逃出了这个学校。也就是说,如果病毒入侵行政楼的时间不小于T,则校长能够成功逃离。
有些时候,校长没有足够的时间逃离,因为病毒到达行政楼的时间太快了。
为了让校长能够逃离学校,不得不拆除校园内的一些道路以延缓行政楼的被入侵时间(拆除道路视为在0 时刻被拆的道路全部消失)。当然,如果使得病毒根本无法到达行政楼,也是可以的。
但是,拆除道路会影响学校的历史气息,且破坏程度定义为拆除的道路中最古老的一条的年龄。请求出保证校长能够安全撤离的情况下,最小的破坏程度。
解
如果删边 只会让最短路变长 就会想到二分
二分+最短路模板 zz题目 但是考试的时候我的策略没有掌握好 就去打 C 题的树剖...
同题目详见 Delay Constrained Maximum Capacity Path
C 避难向导
“特大新闻,特大新闻!全国爆发了一种极其可怕的病毒,已经开始在各个城市中传播开来!全国陷入了巨大的危机!大量居民陷入恐慌,想要逃到其它城市以避难!经调查显示,该病毒来自于C 市的A 学校的一次非法的……”
“哎。”你关上电视,叹了口气。作为A 学校的校长,你一天前为了保命,独自逃离了A 学校,抛弃了全校师生,包括那个曾经帮你计算并拆除道路的工程师。
你良心受到了巨大的谴责,因此决定做出一些补救,回答一些逃难的人提出的询问。
已知该国一共有n 个城市,并且1 号城市是首都。(n-1)条双向的公路连接这些城市,通过这些公路,任意两个城市之间存在且仅存在一条路径。每条公路有一个长度。如果一个城市只与一条公路相连,则称它为边境城市。
该国政府有一个奇怪的规定:每个城市有一个封闭系数di,定义di 为离这个城市最远的边境城市到这个城市的距离。市民们认为,一个城市的安全系数Si 和它的封闭系数有很重要的联系。a,b,c 是该国的幸运数字,所以大家公认一个城市的安全系数Si = (di + a) * b mod c。
市民们一共会提出m 次询问。每个询问包含三个信息,xi,yi 和qi。xi 是询问者所在的城市编号。你要为这个询问者在xi 到yi 的必经之路上找出一个离xi最近的避难城市,并且要求这个避难城市的安全系数大于等于qi。如果存在这样的城市(包含xi 和yi),则输出城市编号,否则输出一行包括一个数-1。
解
安全系数这块就不说了 直接找树的直径
1.树链剖分
想到倍增找点 就想到了树剖
这题其实树剖的思路很好想
每一条链维护一条线段
线段树维护区间最值
- 对于x 到lca 的路径 去倍增 每次首先在靠近x的地方查找 是否有>= 安全系数的 max值 是否存在解 有就递归 否则就看另外一颗子树
- 对于y 到lca 的路径 去倍增 每次首先在靠近lca的地方查找 是否有>= 安全系数的 max值 是否存在解 有就递归 否则就看另外一颗子树
想到lca 这样你就不怕跳过 和 不停交换的问题
关键还有一个地方
不同于直接在全部区间内最右或者最左的最值
就是可能你找的最值不在这个小区间内 可能会 错误
我的解决方法是:
首先递归 回溯的时候查找 你找的这个点是否满足条件就行了
我树剖是自学的 听了hyh 还有 qq 的讲解 我才大概明白了树剖的一些常用技巧
- 求LCA
int lca(int a,int b) {
while(topp[a]!=topp[b]) {
if(dep[topp[a]]<dep[topp[b]]) swap(a,b);
a=f[topp[a]];
}
return dep[a]<dep[b]? a: b;
}
好了放代码
//
#include<stdio.h>
#include<bits/stdc++.h>
#define maxnn 7000000
using namespace std;
#define ll long long
typedef pair <int ,ll> P;
int n,m,a,b,c;
int son[maxnn],f[maxnn];
int las[maxnn],en[maxnn],le[maxnn],tot,nex[maxnn];
ll d1[maxnn],d2[maxnn];
ll xi[maxnn];
int in[maxnn];
ll size[maxnn];
int z[maxnn];
int mark[maxnn];
int d[maxnn];
int cnt=0;
ll w[maxnn];
int dep[maxnn];
int topp[maxnn];
int c1,c2,c3;
struct node {
int a,b;
int val;
} tree[maxnn];
void build(int p,int x,int y) {
tree[p].a=x;
tree[p].b=y;
if(x==y)
tree[p].val=w[x];
if(x<y) {
int mid=(x+y)/2;
build(p<<1,x,mid);
build(p<<1|1,mid+1,y);
tree[p].val=max(tree[p<<1].val,tree[p<<1|1].val);
}
}
int query(int p,int x,int y,int dd,int fla) {
if(tree[p].a==tree[p].b) {
return tree[p].a;
} else {
if(fla%2==0) {
int mid=(tree[p].a+tree[p].b)/2;
if(tree[p<<1|1].val>=dd&&y>mid) {
int tmp= query(p<<1|1,x,y,dd,fla);
if(w[tmp]>=dd) return tmp;
}
if(tree[p<<1].val>=dd&&x<=mid)
{
int tmpp= query(p<<1,x,y,dd,fla);
if(w[tmpp]>=dd) return tmpp;
}
return 0;
}
if(fla%2!=0) {
int mid=(tree[p].a+tree[p].b)/2;
if(tree[p<<1].val>=dd&&x<=mid)
{
int tmpp= query(p<<1,x,y,dd,fla);
if(w[tmpp]>=dd) return tmpp;
}
if(tree[p<<1|1].val>=dd&&y>mid) {
int tmp= query(p<<1|1,x,y,dd,fla);
if(w[tmp]>=dd) return tmp;
}
return 0;
}
}
}
void add(int a,int b,ll c) {
en[++tot]=b;
nex[tot]=las[a];
las[a]=tot;
le[tot]=c;
}
void dfs1(int v,int fa) {
dep[v]=dep[fa]+1;
f[v]=fa;
int maxson=-1;
size[v]=1;
for(int i=las[v]; i; i=nex[i]) {
int u=en[i];
if(u!=fa) {
dfs1(u,v);
size[v]+=size[u];
if(maxson<size[u]) {
son[v]=u;
maxson=size[u];
}
}
}
}
void dfs2(int v,int fa,int ttt) {
topp[v]=ttt;
in[v]=++cnt;
z[cnt]=v;
w[cnt]=xi[v];
if(son[v]) dfs2(son[v],v,topp[v]);
for(int i=las[v]; i; i=nex[i]) {
int u=en[i];
if(u!=fa&&(u!=son[v])) {
dfs2(u,v,u);
}
}
}
int lca(int a,int b) {
while(topp[a]!=topp[b]) {
if(dep[topp[a]]<dep[topp[b]]) swap(a,b);
a=f[topp[a]];
}
return dep[a]<dep[b]? a: b;
}
int ch(int a,int b,ll c) {
int aa=0;
ll fla=0;
int ta=lca(a,b);
fla=0;
while(topp[a]!=topp[ta]) {
if(dep[topp[a]]<dep[topp[ta]]) swap(a,ta);
ll t=query(1,in[topp[a]],in[a],c,fla);
if(t) {
aa=z[t];
return aa;
}
a=f[topp[a]];
}
if(dep[a]<dep[ta]) swap(a,ta);
{
int t=query(1,in[ta],in[a],c,fla);
if(t) {
{
aa=z[t];
return aa;
}
}
}
fla=1;
while(topp[b]!=topp[ta]) {
if(dep[topp[b]]<dep[topp[ta]]) swap(b,ta);
ll t=query(1,in[topp[b]],in[b],c,fla);
if(t) {
aa=z[t];
}
b=f[topp[b]];
}
if(dep[b]<dep[ta]) swap(b,ta);
{
int t=query(1,in[ta],in[b],c,fla);
if(t) {
{
aa=z[t];
return aa;
}
}
}
if(aa) return aa;
return -1;
}
int bfs(int a) {
queue <P > Q;
for(int i=0; i<=n; i++)
mark[i]=0;
ll tmp=0,r=0;
Q.push(make_pair(a,0));
mark[a]=1;
while(Q.size()) {
P s=Q.front();
Q.pop();
for(int i=las[s.first]; i; i=nex[i]) {
int u=en[i];
if(!mark[u]) {
mark[u]=1;
if(tmp<s.second+le[i]) {
tmp=s.second+le[i];
r=u;
}
Q.push(make_pair(u,s.second+le[i]));
}
}
}
return r;
}
void df(ll *ddd,int v,ll l,int fa) {
ddd[v]=l;
for(int i=las[v]; i; i=nex[i]) {
int u=en[i];
if(u!=fa) {
df(ddd,u,l+le[i],v);
}
}
}
int main() {
//freopen("iii.txt","r",stdin);
//freopen("s.out","w",stdout);
scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);
for(int i=1; i<n; i++) {
scanf("%d%d%d",&c1,&c2,&c3);
add(c1,c2,c3);
add(c2,c1,c3);
}
int l=bfs(1);
int r=bfs(l);
df(d1,l,0,0);
df(d2,r,0,0);
for(int i=1; i<=n; i++) {
xi[i]=((max(d1[i],d2[i])+a)*b)%c;
}
dfs1(1,1);
dfs2(1,1,1);
build(1,1,n);
int x,y,z;
for(int i=1; i<=m; i++) {
scanf("%d%d%d",&x,&y,&z);
printf("%d\n",ch(x,y,z));
}
2.倍增的做法
其实和线段树差不多 话说这就是线段树的做法啊
因为是 询问树上的路径问题 所以想到了倍增
对于任意一条路径,都可以从LCA(x,y)中拆开
tr[i][j]表示 从i出发 走\(2^j\)次方 步所到达的最大安全系数
- 二分x到lca 的距离\(2^k\) 看这个区间是否有如果 存在 先递归 \(x \sim x+2^{k-1}\) 再递归剩下区间 找到了就停止
- 二分y到lca 的距离\(2^k\) 看这个区间是否有如果 存在 先递归 \(f[x][k-1] \sim x+2^{k-1}\) 再递归剩下区间 找到了不停止 继续找
或者像求lca
满足就不跳 不满足就跳 最后一定最优
code:
//
#include<stdio.h>
#include<bits/stdc++.h>
#define maxnn 700000
using namespace std;
#define ll long long
typedef pair <int ,ll> P;
int x,y,z,ans;
int n,m,a,b,c;
int son[maxnn];
int las[maxnn],en[maxnn],le[maxnn],tot,nex[maxnn];
ll d1[maxnn],d2[maxnn];
ll xi[maxnn];
int in[maxnn];
ll size[maxnn];
int mark[maxnn];
int d[maxnn];
int cnt=0;
ll w[maxnn];
int tr[maxnn][30];
int dep[maxnn];
int f[maxnn][30];
int c1,c2,c3;
void add(int a,int b,ll c) {
en[++tot]=b;
nex[tot]=las[a];
las[a]=tot;
le[tot]=c;
}
int bfs(int a) {
queue <P > Q;
for(int i=0; i<=n; i++)
mark[i]=0;
ll tmp=0,r=0;
Q.push(make_pair(a,0));
mark[a]=1;
while(Q.size()) {
P s=Q.front();
Q.pop();
for(int i=las[s.first]; i; i=nex[i]) {
int u=en[i];
if(!mark[u]) {
mark[u]=1;
if(tmp<s.second+le[i]) {
tmp=s.second+le[i];
r=u;
}
Q.push(make_pair(u,s.second+le[i]));
}
}
}
return r;
}
void df(ll *ddd,int v,ll l,int fa) {
ddd[v]=l;
for(int i=las[v]; i; i=nex[i]) {
int u=en[i];
if(u!=fa) {
df(ddd,u,l+le[i],v);
}
}
}
void dfs(int v,int fa)
{
dep[v]=dep[fa]+1;
f[v][0]=fa;
tr[v][0]=xi[fa];
int s=ceil(log2(n));
for(int i=1;i<=s;i++)
{
f[v][i]=f[f[v][i-1]][i-1];
tr[v][i]=max(tr[v][i],max(tr[f[v][i-1]][i-1],tr[v][i-1]));
}
for(int i=las[v];i;i=nex[i])
{
int u=en[i];
if(u!=fa)
{
dfs(u,v);
}
}
}
int go_up(int s,int v)
{
int k=log2(n);
for(int i=k;i>=0;i--)
{
if(s&(1<<i))
{
v=f[v][i];
}
}
return v;
}
int lca(int a,int b)
{
if(dep[a]<dep[b]) swap(a,b);
a=go_up(dep[a]-dep[b],a);
if(a==b) return b;
else
{
int k=log2(n);
for(int i=k;i>=0;i--)
{
if(f[a][i]!=f[b][i])
{
a=f[a][i];
b=f[b][i];
}
}
}
return f[a][0];
}
int askx(int x,int k,int z)
{
if(k==0) return tr[x][k]>=z? f[x][0]:-1;
if(tr[x][k]>=z)
{
int tmp=askx(x,k-1,z);
return tmp!=-1?tmp:askx(f[x][k-1],k-1,z);
}
return -1;
}
int asky(int x,int k,int z)
{
if(k==0) return tr[x][k]>=z? f[x][0]:-1;
if(tr[x][k]>=z)
{
int tmp=asky(f[x][k-1],k-1,z);
return tmp!=-1?tmp:asky(x,k-1,z);
}
return -1;
}
int main() {
//freopen("iii.txt","r",stdin);
//freopen("s.out","w",stdout);
scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);
for(int i=1; i<n; i++) {
scanf("%d%d%d",&c1,&c2,&c3);
add(c1,c2,c3);
add(c2,c1,c3);
}
int l=bfs(1);
int r=bfs(l);
df(d1,l,0,0);
df(d2,r,0,0);
for(int i=1; i<=n; i++) {
xi[i]=((max(d1[i],d2[i])+a)*b)%c;
}
dfs(1,1);
while(m--) {
cin>>x>>y>>z;
if(xi[x]>=z) {
cout<<x<<endl;
continue;
}
int u=lca(x,y),ans=-1,tmpy=y;
for(int i=log2(dep[x]); ~i; i--)
if(dep[x]-(1<<i)>=dep[u]) {
ans=askx(x,i,z);
if(ans!=-1) {
printf("%d\n",ans);
break;
}
x=f[x][i];
}
if(ans!=-1)continue;
for(int i=log2(dep[y]); ~i; i--)
if(dep[y]-(1<<i)>=dep[u]) {
int r=asky(y,i,z);
if(ans==-1||(r!=-1&&dep[r]<dep[ans]))ans=r;
y=f[y][i];
}
if(ans==-1)printf("%d\n",xi[tmpy]>=z?tmpy:-1);
else printf("%d\n",ans);
}
}
summary:
打比赛要理性 不能感情用事
老板说过 战场上 要 像一个冷血的得分杀手 理智地拿完自己应该拿的分
而不是 只钻一道题 要找好策略
这次我吃了一个大亏 我记住这个教训了