这道题考察的是数学推导和快速幂取模
从0号开始推,发现只需加上每次走的距离*次数再mod总人数就是答案,其他情况同理。
所以就有公式
a
n
s
ans
ans
=
=
=
(
x
+
m
∗
1
0
k
)
(x+m*10^k)
(x+m∗10k)
m
o
d
mod
mod
n
n
n
AC代码如下
#include<bits/stdc++.h> //转圈游戏
using namespace std;
int main(){
//freopen("circle.in","r",stdin);
//freopen("circle.out","w",stdout);
long long n,m,k,x,tot=1,now=10,ans;
cin>>n>>m>>k>>x;
while(k){
if(k&1) tot=tot*now%n;
k/=2;
now=now*now%n;
}
tot=tot*m%n;
ans=(x+tot)%n;
cout<<ans;
return 0;
}
考试的时候敲完了代码,以为AC了,结果犯了一个细节错误,导致只有30分……
tot*=now%n; //tot=tot*(now%n)
tot=tot*now%n; //这里没有括号
2.火柴排队
通读一遍题后,发现需要进行数学推导。
要求
m
i
n
∑
i
=
1
n
(
a
i
−
b
i
)
2
min\sum_{i=1}^{n}(a_i-b_i)^2
min∑i=1n(ai−bi)2
即求
m
i
n
∑
i
=
1
n
(
a
i
2
−
2
a
i
∗
b
i
+
b
i
2
)
min\sum_{i=1}^{n}(a_i^2-2a_i*b_i+b_i^2)
min∑i=1n(ai2−2ai∗bi+bi2)
由于
∑
i
=
1
n
(
a
i
2
+
b
i
2
)
\sum_{i=1}^{n}(a_i^2+b_i^2)
∑i=1n(ai2+bi2) 为定值
则问题等价于求
m
a
x
∑
i
=
1
n
(
2
a
i
∗
b
i
)
max\sum_{i=1}^{n}(2a_i*b_i)
max∑i=1n(2ai∗bi)
引理:设
a
1
>
a
2
,
b
1
>
b
2
a_1>a_2,b_1>b_2
a1>a2,b1>b2 有:
a
1
∗
b
2
+
a
2
∗
b
1
≤
a
1
∗
b
1
+
a
2
∗
b
2
a_1*b_2+a_2*b_1\leq a_1*b_1+a_2*b_2
a1∗b2+a2∗b1≤a1∗b1+a2∗b2
一番证明后发现,要让大数乘大数,小数乘小数才会有距离最小值,会想到排序。要让我们求最小交换次数,也就是排序后的数组与排序前的数组进行比较,求逆序对的个数,会想到用归并排序或树状数组。这里我使用的是归并排序。
AC代码如下
#include<bits/stdc++.h> //火柴排队
using namespace std;
const int MAXN=100001,mod=99999997;
struct data{ //定义结构体
int x,num;
}a[MAXN],b[MAXN];
int step[MAXN],q[MAXN],n,tot=0;
bool compare(data a,data b){ //比较函数
return a.x<b.x;
}
void gb(int l,int r){ //归并排序
if(l==r) return;
int mid=(l+r)/2;
gb(l,mid);
gb(mid+1,r);
int vis=l,i=l,j=mid+1;
while(i<=mid&&j<=r){
if(step[i]<=step[j]){
q[vis]=step[i];
vis++,i++;
}
else{
q[vis]=step[j];
vis++,j++;
tot=(tot+(mid-i+1))%mod;
}
}
while(i<=mid){
q[vis]=step[i];
i++,vis++;
}
while(j<=r){
q[vis]=step[j];
j++,vis++;
}
for(int i=l;i<=r;i++){
step[i]=q[i];
}
}
int main(){
//freopen("match.in","r",stdin);
//freopen("match.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i].x);
a[i].num=i;
}
for(int i=1;i<=n;i++){
scanf("%d",&b[i].x);
b[i].num=i;
}
sort(a+1,a+n+1,compare);
sort(b+1,b+n+1,compare);
for(int i=1;i<=n;i++){
step[a[i].num]=b[i].num;
}
gb(1,n);
cout<<tot;
return 0;
}
考试的时候没想到正解(图论快忘完了),用的dfs深搜,结果爆零。后面经过大佬的讲解才完全懂了 ,就当复习知识点。
为什么要用最大生成树?
如图,有三条边,1-2,1-3,2-3,边权分别为45,1,50,由题目要求,我们肯定会选择走1-2,2-3这条路径,可以载运更多的货物,所以我们推出要求的是最大生成树。
由于有的数据它的图不一定联通,需要加入并查集来判断。
AC代码如下
#include<bits/stdc++.h> //货车运输
#define MAXN 10005
#define INF 1e8
using namespace std;
struct data{
int x,y,dis;
}a[5*MAXN];
struct node{
int to,next,dis;
}b[10*MAXN];
int cnt,n,m,head[MAXN],deep[MAXN],f[MAXN],fa[MAXN][21],dis[MAXN][21];
bool vis[MAXN];
void addedge(int from, int to, int dis){ //链式前向星存图
b[++cnt].next=head[from];
b[cnt].to=to;
b[cnt].dis=dis;
head[from]=cnt;
return;
}
bool compare(data a,data b){
return a.dis>b.dis;
}
int find(int x){
if(f[x]!=x) f[x]=find(f[x]);
return f[x];
}
void kruskal(){ //最大生成树
sort(a+1,a+m+1,compare);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++)
if(find(a[i].x)!=find(a[i].y)){
f[find(a[i].x)]=find(a[i].y);
addedge(a[i].x,a[i].y,a[i].dis);
addedge(a[i].y,a[i].x,a[i].dis);
}
return;
}
void dfs(int node){
vis[node]=true;
for(int i=head[node];i;i=b[i].next){
int to=b[i].to;
if(vis[to]) continue;
deep[to]=deep[node]+1;
fa[to][0]=node;
dis[to][0]=b[i].dis;
dfs(to);
}
return;
}
int lca(int x, int y){ //最近公共祖先
if(find(x)!=find(y)) return -1;
int ans=INF;
if(deep[x]>deep[y]) swap(x,y);
for(int i=20;i>=0;i--)
if(deep[fa[y][i]]>=deep[x]){
ans=min(ans,dis[y][i]);
y=fa[y][i];
}
if(x==y) return ans;
for(int i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i]){
ans=min(ans,min(dis[x][i],dis[y][i]));
x=fa[x][i];
y=fa[y][i];
}
ans=min(ans,min(dis[x][0],dis[y][0]));
return ans;
}
int main(){
int x,y,z,q;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
a[i].x=x,a[i].y=y,a[i].dis=z;
}
kruskal();
for(int i=1;i<=n;i++)
if(!vis[i]){
deep[i]=1;
dfs(i);
fa[i][0]=i;
dis[i][0]=INF;
}
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++){
fa[j][i]=fa[fa[j][i-1]][i-1];
dis[j][i]=min(dis[j][i-1],dis[fa[j][i-1]][i-1]);
}
cin>>q;
for(int i=1;i<=q;i++){
scanf("%d%d",&x,&y);
printf("%d\n",lca(x,y));
}
return 0;
}