全国大学生算法竞赛秋季赛习题
Problem A 智慧果
题面
已知序列 a:
a 1 = 1 , a 2 = 2 , a 3 = 3 a_1=1,a_2=2,a_3=3 a1=1,a2=2,a3=3
a i = a i − 1 a i − 2 a i − 3 + a i − 1 + a i − 2 + a i − 3 a_i=a_{i−1}a_{i−2}a_{i−3}+a_{i−1}+a_{i−2}+a_{i−3} ai=ai−1ai−2ai−3+ai−1+ai−2+ai−3
求 a 1000 a_{1000} a1000 对 100000 取模的值。
题解
暴 力 计 算 每 一 项 对 100000 取 模 的 值 即 可 。 暴力计算每一项对 100000 取模的值即可。 暴力计算每一项对100000取模的值即可。
时 间 复 杂 度 O ( n ) , 其 中 n 为 项 数 。 时间复杂度 O(n),其中 n 为项数。 时间复杂度O(n),其中n为项数。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline 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*10+ch-'0',ch=getchar();
return s*w;
}
const int p=100000;
// int qp(int x,int y)
// {
// int res=1;
// for(int t=x; y; y>>=1,t=t*t%p) if(y&1) res=res*t%p;
// return res;
// }//快速幂模板,写着玩玩,在这题不用
int a[100003];
signed main()
{
a[1]=1;
a[2]=2;
a[3]=5;//初始化
for(int i=4; i<=1000; ++i)
{
a[i]=a[i-1]*a[i-2]*a[i-3]+a[i-1]+a[i-2]+a[i-3];//带入公式
a[i]%=p;//暴力取模
}
printf("%lld\n",a[1000]);
return 0;
}
Problem B Xanadu#
题面
给 定 一 个 n × n 的 01 矩 阵 给定一个 n×n 的 01 矩阵 给定一个n×n的01矩阵, 和 n 个 点 的 图 。 你 可 以 花 费 1 的 代 价 和 n 个点的图。你可以花费 1 的代价 和n个点的图。你可以花费1的代价 将 矩 阵 中 的 一 个 1 变 成 0 。 将矩阵中的一个 1 变成 0。 将矩阵中的一个1变成0。
操 作 完 成 后 , 记 第 i 行 从 左 到 右 第 一 个 为 1 的 位 置 为 j , 操作完成后,记第 i 行从左到右第一个为 1 的位置为 j, 操作完成后,记第i行从左到右第一个为1的位置为j, 则 在 图 上 从 i 到 j 之 间 连 一 条 有 向 边 。 则在图上从 i 到 j 之间连一条有向边。 则在图上从i到j之间连一条有向边。
(言下之意,为利用稀疏矩阵表示拓扑图)
问至少要花费多少代价,才能让图上存在一条从 1 到 n 的路径,如果无法达成输出 −1。
n ≤ 300 n≤300 n≤300
题解
基环树:无向图,一个环,环上每个点都是树根
不难发现最后得到的一定是一棵基环树,因此如果有路径,必然存在一条无环的路径,即每个点仅经过一次。
我们对于矩阵建图。假设第 i i i 行的第 j j j 个位置是该行第 k 个 1 k 个 1 k个1,我们就从 i 到 j 建 一 条 费 用 为 k − 1 i 到 j 建一条费用为 k−1 i到j建一条费用为k−1 的边。
不难发现,最后得到的每条从 1 到 n 的路径的边权总和就是这个方案要花费的代价,因此直接在原图上跑最短路即可。
时间复杂度 O ( n 2 ) O(n^2) O(n2)或 O ( n 2 l o g n ) O(n^2logn) O(n2logn)。
简而言之,就是最短路径问题!
注意本题是有向图,因此矩阵不是对称矩阵。
样例
输入
4
0 1 0 0
0 0 1 0
0 0 0 1
0 0 0 0
输出
0//因为已经连通了
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline 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*10+ch-'0',ch=getchar();
return s*w;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>> > q;//把STL用到极致了
vector<pair<int,int> > v[1000003];
int dis[1000003];
bool vis[1000003];
signed main()//int main的另一种写法
{
int n=read();//nxn矩阵
int s=1;
memset(dis,0x3f,sizeof(dis));//初始化距离为最大值
for(int i=1; i<=n; i++)
{
int s=0;
for(int j=1;j<=n;j++)
{
int x=read();
if(x==0)continue;
v[i].push_back(make_pair(j,s));//读入某列,然后是第几个,
//这个第几个是要干嘛呢?
s++;
}
}
dis[s]=0/*最后点数为什么是0?*/,q.push(make_pair(0,s));
while(!q.empty())
{
int x=q.top().second;
q.pop();
if(vis[x]) continue;
vis[x]=1;
for(pair<int,int> y:v[x])
if((!vis[y.first])&&dis[y.first]>dis[x]+y.second)
dis[y.first]=dis[x]+y.second,
q.push(make_pair(dis[y.first],y.first));
}
if(dis[n]>1e12) puts("-1");
else printf("%lld\n",dis[n]);
return 0;
}
Problem C 这是一道大难题
题面
给定一张图,求其必须包含给定一条边的最小生成树。
n ≤ 1 0 5 , m ≤ 2 × 1 0 5 n≤10^5,m≤2×10^5 n≤105,m≤2×105。
题解
直接先加入这条边,然后跑 K r u s k a l Kruskal Kruskal算法即可。
正确性显然。
时间复杂度为 O ( m l o g m + n α ( n ) ) O(mlogm+nα(n)) O(mlogm+nα(n))。
代码
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native")
/* 编译优化的奇技淫巧
1 #pragma comment(linker, "/stack:200000000")
2 #pragma GCC optimize("Ofast,no-stack-protector")
3 #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
4 #pragma GCC optimize("unroll-loops")
*/
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <bitset>
#define od(x) printf("%d",x)
#define odb(x) printf("%d ",x)
#define odl(x) printf("%d\n",x)
#define odp(x,y) printf("%d %d\n",x,y)
#define ol(x) puts("")
#define old(x) printf("%lld",x)
#define oldb(x) printf("%lld ",x)
#define oldl(x) printf("%lld\n",x)
#define oldp(x,y) printf("%lld %lld\n",x,y)//plus
#define rg(x) for(int i=1;i<=(x);i++){
#define gr }
#define rrg(x) for(int i=0;i<(x);i++)
#define rdln(a) a[i]=read();
#define rdln0(a,x) rrg(x) rdln(a) gr
#define rdln1(a,x) rg(x) rdln(a) gr
#define int long long
inline int read()//自定义一个读入
{
int num=0,f=1;char c=getchar();
while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
while(c>47&&c<58)num=num*10+(c^48),c=getchar();
return num*f;
}
inline int re1d()
{
char c=getchar();
while(c<48||c>49)c=getchar();
return c&1;
}
inline int min(int a,int b){return a>b?b:a;}
inline int max(int a,int b){return a<b?b:a;}
int fa[200005];
inline int find(int x)
{
return x==fa[x]?x:fa[x]=find(fa[x]);
}
inline int uni(int x,int y)
{
int fx=find(x),fy=find(y);
if(fx==fy)return 0;
fa[fx]=fy;
return 1;
}
struct e{
int u,v,w;
bool operator<(const e &b)const
{
return w<b.w;
}
}a[200005];
signed main()
{
int n=read(),m=read();
long long c=0;
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++)a[i].u=read(),a[i].v=read(),a[i].w=read();
int x=read();
c+=uni(a[x].u,a[x].v)*a[x].w;
std::sort(a+1,a+1+m);
for(int i=1;i<=m;i++)c+=uni(a[i].u,a[i].v)*a[i].w;
oldl(c);
return 0;
}
Problem D zeal
题面
给 定 长 度 为 n 的 序 列 a , m 次 求 [ l , r ] 中 出 现 k 次 的 元 素 数 量 。 给定长度为 n的序列 a,m 次求 [l,r] 中出现 k 次的元素数量。 给定长度为n的序列a,m次求[l,r]中出现k次的元素数量。
n , m , a i ≤ 4 × 1 0 4 n,m,a_i≤4×10^4 n,m,ai≤4×104
题解
直接上莫队算法即可。
#### 样例
输入
10 2
1 2 3 1 1 2 1 2 3 1
1 5 3
2 9 3
输出
1
2
时间复杂度 O ( n m ) O(n\sqrt{m}) O(nm
文章来源: blog.csdn.net,作者:irrationality,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/weixin_54227557/article/details/120923053
- 点赞
- 收藏
- 关注作者
评论(0)