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)。
代码
#include<bits/stdc++.h>
#define int long long
#define pb push_back
#define mp make_pair
#define x first
#define y second
#define WT int T=read();while(T--)
#define NO puts("NO");
#define YES puts("YES");
using namespace std;
//add sub 莫队算法的离线处理
inline int read()
{
char c=getchar();int x=0;bool f=0;
for(;!isdigit(c);c=getchar())f^=!(c^45);
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
if(f)x=-x;return x;
}
const int M=5e4+10;
int n,Q,a[M],sum1[M],sum2[M],bl,ans[M];
void add(int x)
{
sum2[sum1[x]]--;
sum1[x]++;
sum2[sum1[x]]++;
}
void del(int x)
{
sum2[sum1[x]]--;
sum1[x]--;
sum2[sum1[x]]++;
}
struct query
{
int l,r,k,p;
}q[M];
int id(int x){return (x-1)/bl;}
bool cmp(query a,query b)
{
return id(a.l)<id(b.l)||(id(a.l)==id(b.l)&&id(a.r)<id(b.r));
}
signed main()
{
n=read(),Q=read();bl=sqrt(n);
for (int i=1;i<=n;i++)a[i]=read();
for (int i=1;i<=Q;i++)
q[i].l=read(),q[i].r=read(),q[i].k=read(),q[i].p=i;
sort(q+1,q+1+Q,cmp);int l=1,r=0;
for (int i=1;i<=Q;i++)
{
while(r<q[i].r)add(a[++r]);
while(l>q[i].l)add(a[--l]);
while(l<q[i].l)del(a[l++]);
while(r>q[i].r)del(a[r--]);
ans[q[i].p]=sum2[q[i].k];
}
for (int i=1;i<=Q;i++)
cout<<ans[i]<<endl;
return 0;
}
Problem E Alice 大战 Bob
题面
给定一个长度为 n 的序列,有 m 次询问。
每次询问给定 l,r,Alice 和 Bob 依次操作,每次操作可以将 l 加 1 或者 r 减 1。操作后 [l,r] 内的数若单调不升或单调不降,操作者获胜。
n , m ≤ 1 0 6 n,m≤10^6 n,m≤106。
题解
做法和 AGC002E 十分类似。
先把所有已经单调不升或单调不降的区域求出。
然后把一步能走到这些区域的位置求出。
于是问题变成了 AGC002E,可以自行查阅该题题解。
时间复杂度为 O(n+m) 或 O(n+mlogn)。