A
题面:
题目大意:
思路
想直接算的话,直接暴毙O(∩_∩)O哈哈~
数据范围达到10^15。
一个线性递推式,递推到很大的某一项,祭出:矩阵快速幂!
AC代码
#include<bits/stdc++.h>
#define pb push_back
#define lowbit(x) x&(-x)
#define mst(a,b) memset(a,b,sizeof a)
#define fi first
#define se second
#define mp make_pair
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
const int Mod = 1e6;
const int N = 1e5+10;
ll Read(){
ll val = 0, opt = 1; char ch;
while (!isdigit(ch = getchar())) if (ch == '-') opt = -1;
while (isdigit( ch )) (val *= 10) += ch - '0', ch = getchar();
return val * opt;
}
struct mat{
int n;
vector<vector<long long> > val;
//构造函数
explicit mat(int sz) : val(sz,vector<long long>(sz)){
n=sz;
}
//单位矩阵
void emat(){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
val[i][j]= (i==j);
}
}
}
//重载乘号
mat operator * (const mat &a) const{
mat res(n);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
for(int k=0;k<n;k++){
res.val[i][j]=(res.val[i][j]+val[i][k]*a.val[k][j])%Mod;
}
}
}
return res;
}
};
//快速幂
mat matqp(mat a,long long b){
mat res(a.n);
res.emat();
while(b){
if(b&1) res=res*a;
a=a*a;
b>>=1;
}
return res;
}
signed main(){
ios::sync_with_stdio(false);
ll n,k,l;
cin>>n>>k>>l;
n/=5;
k%=Mod;l%=Mod;
mat tran(2);
tran.val[0][0]=k;tran.val[0][1]=l;
tran.val[1][0]=1;tran.val[1][1]=0;
mat res = matqp(tran, n-1);
ll ans = (res.val[1][0])*(k*k+l)+(res.val[1][1])*k;
ans%=Mod;
printf("%06d\n", (int)ans);
return 0;
}
J
题目
题目大意
给定一个有边权的连通图,求询问任意两点之间路径的最小边权的最大值。
思路
看到题面我立马想到了最大流,并很快反应过来自己不会最大流,现在也不会,无法讲出一个所以然(/ω\)
听了讲评之后,发现跟前一场补的题特别像,但是这场之后才补的题,,,这逻辑
1、容易发现所有经过的路径应该包含在一棵最大生成树中,用kruskal简化图。
2、询问两点的路径上的最小边权,答案是确定的,可该如何计算?
用LCA的思想,在倍增的过程中维护树上区间的最小值。
f[j][i+1] = f[ f[j][i] ][i];
mx[j][i+1] = min(mx[j][i], mx[ f[j][i] ][i]);
另解:
这题还可以用启发式合并(dsu),将询问绑到点上,将边从大到小加入并查集。合并的时候,若小集合中有和大集合相同的询问,则可以以当前加入的边的边权w回答该询问。
AC代码
#include<bits/stdc++.h>
#define pb push_back
#define lowbit(x) x&(-x)
#define mst(a,b) memset(a,b,sizeof a)
#define fi first
#define se second
#define mp make_pair
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
const int Mod = 1e9+7;
const int N = 1e5+10;
ll Read(){
ll val = 0, opt = 1; char ch;
while (!isdigit(ch = getchar())) if (ch == '-') opt = -1;
while (isdigit( ch )) (val *= 10) += ch - '0', ch = getchar();
return val * opt;
}
struct node{
int st,ed,w;
}edge[N];
int fa[N];
int n,m,s;
vector<pair<int, int> > v[N];
int mx[N][30],f[N][30];
int dep[N];
bool cmp(node a,node b){
return a.w > b.w;
}
int Find(int x){
if(x == fa[x]) return x;
else return fa[x] = Find(fa[x]);
}
void ini(){
for(int i=1;i<=n;i++) fa[i]=i;
mst(dep, 0);
mst(mx, INF);
mst(f, -1);
}
void Union(int x, int y,int w){
int f1 = Find(x);
int f2 = Find(y);
if(f1 != f2){
fa[f1] = fa[f2];
v[f1].pb(mp(f2, w));
v[f2].pb(mp(f1, w));
}
}
void dfs(int u, int fa){
dep[u] = dep[fa] + 1;
f[u][0] = fa;
for(auto i : v[u]){
if(i.fi == fa) continue;
mx[i.fi][0] = i.se;
dfs(i.fi, u);
}
}
int LCA(int x,int y){
int ans = INF;
if(dep[x] < dep[y]) swap(x,y);
for(int i=25;i>=0;i--){
if(f[x][i] != -1 && dep[ f[x][i] ] >= dep[y]){
ans = min(ans, mx[x][i]); //跳跃则更新答案
x = f[x][i];
}
if(x == y) return ans;
}
for(int i=25;i>=0;i--)
{
if(f[x][i] != f[y][i]){
ans = min(ans, mx[x][i]);
ans = min(ans, mx[y][i]);
x=f[x][i];y=f[y][i];
}
}
ans = min(ans, mx[x][0]);
ans = min(ans, mx[y][0]);
return ans;
}
signed main(){
// printf("%d", INF);
n=Read();m=Read();s=Read();
ini();
for(int i=0;i<m;i++){
edge[i].st=Read();
edge[i].ed=Read();
edge[i].w=Read();
}
sort(edge, edge + m, cmp);
//保存最大生成树
for(int i=0;i<m;i++) Union(edge[i].st, edge[i].ed, edge[i].w);
dfs(1,0);
for(int i=0;i<30;i++){
for(int j=1;j<=n;j++){
if(f[j][i] != -1){
f[j][i+1] = f[ f[j][i] ][i];
mx[j][i+1] = min(mx[j][i], mx[ f[j][i] ][i]);
}
}
}
while(s--){
int a,b;
a=Read();b=Read();
cout<<LCA(a, b)<<endl;
}
return 0;
}
另解
#include<bits/stdc++.h>
#define pb push
#define lowbit(x) x&(-x)
#define mst(a,b) memset(a,b,sizeof a)
#define fi first
#define se second
#define mp make_pair
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
const int Mod = 1e9+7;
const int N = 1e5+10;
ll Read(){
ll val = 0, opt = 1; char ch;
while (!isdigit(ch = getchar())) if (ch == '-') opt = -1;
while (isdigit( ch )) (val *= 10) += ch - '0', ch = getchar();
return val * opt;
}
pair<int, int> e[N]; //存边的始终点
priority_queue<pair<int, int> > pq; //存边权和边序号
int fa[N];
set<int> re[N]; //询问集合
int ans[N];
int Find(int u){
if(u == fa[u]) return u;
else return fa[u] = Find(fa[u]);
}
signed main(){
int n,m,q;
n=Read();m=Read();q=Read();
int st,ed,w;
for(int i=1;i<=m;i++){
st=Read();ed=Read();w=Read();
e[i].fi=st;
e[i].se=ed;
pq.pb(mp(w, i));
}
for(int i=1;i<=q;i++){
st=Read();ed=Read();
re[st].emplace(i);
re[ed].emplace(i);
}
for(int i=1;i<=n;i++) fa[i]=i;
int cnt=0;
while(!pq.empty() && q > cnt){
pair<int, int> p = pq.top();pq.pop();
int f1=Find(e[p.se].fi),f2=Find(e[p.se].se);
if(f1 == f2) continue;
if(re[f1].size() > re[f2].size()) swap(f1, f2);
//将f1并入f2
fa[f1] = f2;
for(auto i : re[f1]){
if(re[f2].find(i) != re[f2].end()){
ans[i] = p.fi;
re[f2].erase(i);
cnt++; //回答数+1
}
else{
re[f2].emplace(i);
}
}
re[f1].clear();
}
for(int i=1;i<=q;i++) printf("%d\n", ans[i]);
return 0;
}