写在前面
二区教练:啊培训了这么多天,我和三区教练决定出套题来考试。
然后他妈的搬了 HN 某中学某学生出的题,还他妈和培训内容屁关系没有。
笑了。
预期:100 + 100 + 40 = 240
实际:15 + 100 + 20 = 135
开题顺序 1 3 2。
T1 是矩阵板子,开场五分钟样例过了就扔了,结果 n-1
写成 n
多跑了一次快速幂导致挂 85 分。我是傻逼。
后来问了问同样挂 T1 的 zzg,发现他是少跑了一次快速幂,笑死。
T2 首先想到是个 (O(1)) 结论题,然后开始人工打表找规律,结果开始看错题,找了一个半小时才结束。
T3 打的部分分,正解想到了但是以为只能 DP 做,结果还写挂了。
caq AK orz.
所以我就是次次模拟赛挂 T1 的神!
这个故事告诉我们,男人不能太自信。
正文
T1
(f[1]=f[2]=1)
(f[n]=(A imes f[n-1]+B imes f[n-2])mod 7)
给定 (A,B,n),求 (f[n])。
(nle 21,4748,3648。)
“这个模数小,肯定有诈啊!”——szt。
做法 1:矩阵板子直接上就行了。注意模数小,所以样例不可信啊!!!
做法 2:模数小,手模几个数可以发现,答案是一个周期函数,然后求就可以。
我也猜到了与周期有关,但是感觉没有写矩阵来的实在。
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 100010
#define INF 0x3f3f3f3f
#define int long long
#define Mod 7
using namespace std;
int A,B,n;
struct Matrix{
int z[20][20];
Matrix(){memset(z,0,sizeof z);}
Matrix operator * (const Matrix &b){
Matrix ans;
for(int i=1;i<=2;i++)
for(int j=1;j<=2;j++)
for(int k=1;k<=2;k++)
ans.z[i][k]=(ans.z[i][k]+(z[i][j]%Mod)*(b.z[j][k]%Mod))%Mod;
return ans;
}
}jz,res,all;
int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
return s*w;
}
void quickpow(int y){
while(y){
if(y&1) res=res*jz;
jz=jz*jz;y>>=1;
}
}
signed main(){
// freopen("attack.in","r",stdin);
// freopen("attack.out","w",stdout);
A=read();B=read();n=read();
jz.z[1][1]=A;jz.z[1][2]=1;jz.z[2][1]=B;
res.z[1][1]=res.z[1][1]=1;
quickpow(n-1);printf("%lld
",res.z[1][2]);
return 0;
}
T2
我草图片怎么这么大。
(T) 组询问,(Tle 9008,0le n,mle 20000.)
啊这个 (O(nT)) 的复杂度都过不了,那这个肯定是 (O(1)) 出答案。
要不来猜结论?一开始看错题,看成 从第 (i) 个人开始往前到每一个位置物理老师都不能小于生物老师。
怀疑题目出错了,感觉这显然不可做啊。后来发现读错题了,推出个 (large{frac{(2 imes m + 1)}{(m+n)!}^{(n-m)}}) 感觉蛮对的,但是 (O(1)) 搞不了。
于是大力打表,搞了 6 + 24 + 120 + 720 = 870 种情况。发现结果如下:
瞪眼法找规律
n m -> ans
1 0 -> 1
1 1 -> 1/2
2 1 -> 4/6 = 2/3
2 2 -> 8/24 = 1/3
3 1 -> 18/24 = 3/4
3 2 -> 60/120 = 2/4
3 3 -> 180/720 = 1/4
我估计不是傻逼都能看出来吧。答案是 (large{frac{n-m+1}{n+1}})。
然后讲讲怎么想出来。
可以将原问题转化一下,看成是在一个二维平面上行走,物理老师看成移动 ((1,0)),生物老师看成移动 ((0,1))。
那么到达 ((n,m)) 点且路线又不走到 (y=x) 这条直线上方的路线总数就是答案。
这个组合问题很经典,方案数为 (egin{pmatrix} m+n\ m end{pmatrix} - egin{pmatrix} m+n \ m-1 end{pmatrix})。
化简完答案同上。
核心代码就一行,就不放了。
T3
给出 (n) 个点,(m) 条带权双向边,假设同一个双联通分量中的点不计边权,问每个点到其他点的最远距离。
(1le nle 20000,1le mle 200000)。
首先想到双联通分量缩点,然后考虑怎么找最长。
发现缩完后是一棵树,然后决定跑最短路(雾。其实是想先拿前四十分的。结果最后 T2 写完就到点了。
szt 表示可以跑树剖 + 树形 DP + 换根,我直接问号。
其实有想到是求树的直径两点中较远的那个,但是以为 dfs 求也过不了,DP 又没自信写,就这么过去了。
然后发现 AK 神 caq dfs 过了。
好了扯远了。这题其实只要 dfs/DP 出树的直径的两个点 (a,b),然后答案就是 (max(dis_{a,x},dis_{b,x}))。
就没了。
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 2001000
#define INF 0x3f3f3f3f
//#define int long long
using namespace std;
bool vis[maxn];
int n,tot=1,cnt,top,t,m,TOT,Max,rt;
int num[maxn],val[maxn],Head[maxn];
int low[maxn],head[maxn],ans[maxn],Ans[maxn];
int Dis[maxn][3],siz[maxn],zhan[maxn],dfn[maxn];
struct edge{int fr,to,dis,nxt;}e[maxn*20],E[maxn*20];
int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
return s*w;
}
void add(int fr,int to,int dis){
e[++tot].fr=fr;e[tot].to=to;e[tot].dis=dis;
e[tot].nxt=head[fr];head[fr]=tot;
}
void Add(int fr,int to,int dis){
E[++TOT].fr=fr;E[TOT].to=to;
E[TOT].dis=dis;E[TOT].nxt=Head[fr];
Head[fr]=TOT;
}
void tarjan(int u){
low[u]=dfn[u]=++cnt;
zhan[++top]=u;
for(int i=head[u];i;i=e[i].nxt){
if(!vis[i]){
vis[i]=vis[i^1]=1;int to=e[i].to;
if(!dfn[to]) tarjan(to),low[u]=min(low[u],low[to]);
else low[u]=min(low[u],dfn[to]);
}
}
if(dfn[u]==low[u]){
++siz[++t];
int pre=zhan[top--];
num[pre]=t;
while(pre!=u){
++siz[t];
pre=zhan[top--];
num[pre]=t;
}
}
}
void dfs1(int u,int fa){
for(int i=Head[u];i;i=E[i].nxt){
int to=E[i].to;
if(to==fa) continue;
Dis[to][1]=Dis[u][1]+E[i].dis;
if(Dis[to][1]>Max) Max=Dis[to][1],rt=to;
dfs1(to,u);
}
}
void dfs2(int u,int fa){
for(int i=Head[u];i;i=E[i].nxt){
int to=E[i].to;
if(to==fa) continue;
Dis[to][2]=Dis[u][2]+E[i].dis;
dfs2(to,u);
}
}
int main(){
// freopen("prize2.in","r",stdin);
// freopen("prize.out","w",stdout);
n=read();m=read();
for(int i=1,fr,to,dis;i<=m;i++){
fr=read();to=read();dis=read();
add(fr,to,dis);add(to,fr,dis);
}
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
for(int i=1;i<=n;i++)
for(int j=head[i];j;j=e[j].nxt){
int to=e[j].to;
if(num[i]!=num[to]) Add(num[i],num[to],e[j].dis);
}
dfs1(1,-1);memset(Dis,0,sizeof Dis);Max=0;
dfs1(rt,-1);Max=0;dfs2(rt,-1);
for(int i=1;i<=n;i++)
printf("%d
",max(Dis[num[i]][1],Dis[num[i]][2]));
return 0;
}
写在后面
自信 + 不自信 + 菜 + naive 是这次挂分的原因。
还是需要多练啊/kk。
以后看到这个傻逼记得提醒他检查 T1。