Loading

6.13 模拟赛结题报告

写在前面

二区教练:啊培训了这么多天,我和三区教练决定出套题来考试。

然后他妈的搬了 HN 某中学某学生出的题,还他妈和培训内容屁关系没有。

笑了。

预期:100 + 100 + 40 = 240

实际:15 + 100 + 20 = 135

开题顺序 1 3 2。

T1 是矩阵板子,开场五分钟样例过了就扔了,结果 n-1 写成 n 多跑了一次快速幂导致挂 85 分。我是傻逼。

后来问了问同样挂 T1 的 zzg,发现他是少跑了一次快速幂,笑死。

/cy

T2 首先想到是个 \(O(1)\) 结论题,然后开始人工打表找规律,结果开始看错题,找了一个半小时才结束。

/cy

T3 打的部分分,正解想到了但是以为只能 DP 做,结果还写挂了。

/cy

caq AK orz.

所以我就是次次模拟赛挂 T1 的神!

这个故事告诉我们,男人不能太自信。

\[\]

正文

T1

\(f[1]=f[2]=1\)
\(f[n]=(A\times f[n-1]+B\times f[n-2])\bmod 7\)
给定 \(A,B,n\),求 \(f[n]\)
\(n\le 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\n",res.z[1][2]);
  return 0;
}

T2

我草图片怎么这么大。

\(T\) 组询问,\(T\le 9008,0\le n,m\le 20000.\)

啊这个 \(O(nT)\) 的复杂度都过不了,那这个肯定是 \(O(1)\) 出答案。

要不来猜结论?一开始看错题,看成 从第 \(i\) 个人开始往前到每一个位置物理老师都不能小于生物老师。

怀疑题目出错了,感觉这显然不可做啊。后来发现读错题了,推出个 \(\large{\frac{(2\times 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\) 这条直线上方的路线总数就是答案。
这个组合问题很经典,方案数为 \(\begin{pmatrix} m+n\\ m \end{pmatrix} - \begin{pmatrix} m+n \\ m-1 \end{pmatrix}\)
化简完答案同上。

核心代码就一行,就不放了。

T3

给出 \(n\) 个点,\(m\) 条带权双向边,假设同一个双联通分量中的点不计边权,问每个点到其他点的最远距离。
\(1\le n\le 20000,1\le m\le 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\n",max(Dis[num[i]][1],Dis[num[i]][2]));
  return 0;
}

\[\]

写在后面

自信 + 不自信 + 菜 + naive 是这次挂分的原因。

还是需要多练啊/kk。

以后看到这个傻逼记得提醒他检查 T1。

posted @ 2021-06-14 09:51  KnightL  阅读(43)  评论(0编辑  收藏  举报