题解
其实今天的题目不是很难…但是内存炸了第一题就爆零了orz
第一题——八数码
【题目描述】
- 给出你两个九宫格包含数字0-8,每次操作能够将0进行上下左右某个方向上的交换,问最少多少次交换到目标的情况。达不到就输出-1。
- emmm其实是一道万年老题,当年学bfs的时候打过一次,关键在于去重,hash压缩之后进行bfs。
- 但是我忘记开map了,而是本机测的时候开了一个 9 ∗ 1 0 8 9*10^8 9∗108的布尔数组,就差不多3个G吧…
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
const int N=410000;
void fff(){
freopen("eight.in","r",stdin);
freopen("eight.out","w",stdout);
}
int sta[9],tar[9],ta;
map<int,int>mp;
struct que{
int a[N],ss[N];
int head,tail;
void init(){
memset(a,0,sizeof(a));
head=tail=0;
}
bool empty(){
return head==tail;
}
void push(int x,int step){
tail=(tail+1)%N;
a[tail]=x;
ss[tail]=step;
}
int top_a(){
return a[head+1];
}
int top_step(){
return ss[head+1];
}
void pop(){
head++;
head%=N;
}
}q;
inline int HASH(int *a){
int x=a[1];
for(int i=2;i<=9;i++){
x=x*10;
x+=a[i];
}
return x;
}
int main(){
// fff();
for(int i=1;i<=9;i++) scanf("%d",&sta[i]);
for(int i=1;i<=9;i++) scanf("%d",&tar[i]);
int tt=HASH(sta);
ta=HASH(tar);
q.push(tt,0);
while(!q.empty()){
int t1=q.top_a(),t2=q.top_step();q.pop();
if(t1==ta){
printf("%d",t2);
return 0;
}
if(mp[t1]) continue;
mp[t1]++;
int pos;
for(int i=9;i>=1;i--){
sta[i]=t1%10;
t1/=10;
if(sta[i]==0) pos=i;
}
if(pos>3){swap(sta[pos],sta[pos-3]);q.push(HASH(sta),t2+1);swap(sta[pos],sta[pos-3]);}
if(pos<=6){swap(sta[pos],sta[pos+3]);q.push(HASH(sta),t2+1);swap(sta[pos],sta[pos+3]);}
if(pos%3!=0){swap(sta[pos],sta[pos+1]);q.push(HASH(sta),t2+1);swap(sta[pos],sta[pos+1]);}
if(pos%3!=1){swap(sta[pos],sta[pos-1]);q.push(HASH(sta),t2+1);swap(sta[pos],sta[pos-1]);}
}
puts("-1");
}
第二题——多段线性函数
【题目描述】
- 给出函数 f [ y ] = ∑ i = 1 n ∣ y − x i ∣ f[y]=\sum_{i=1}^{n}{ \left| y-x_i \right|} f[y]=∑i=1n∣y−xi∣以及n个x的取值范围 [ l i , r i ] [l_i,r_i] [li,ri],求出使得函数求出最小值的y的区间。
- 可证明y的区间只有一段,并且当y只能取一个值z时, L = R = z L=R=z L=R=z
-
其实这道题数学建模之后题目就很好理解了,就是给出n段x的区间,x在每段区间上可以随便取,然后在数轴上取一个点y,到这写点的举例最少。
-
大概就是长下面这样:
-
然后你就会发现函数其实就是一个区间距离函数了,可以改写了:
f [ x ] = ∑ i − 1 n { y − r i r i < y 0 l i ≤ y ≤ r i l i − y l i > y f[x]=\sum_{i-1}^{n}\begin{cases} y-r_i & r_i<y \\ 0 &l_i\leq y \leq r_i \\ l_i-y & l_i> y\\ \end{cases} f[x]=∑i−1n⎩⎪⎨⎪⎧y−ri0li−yri<yli≤y≤rili>y -
然后你就会发现这个函数好像是一个单峰函数,还有可能是个平底锅…
-
然后果断两次二分枚举(一次是 < < <,一次是 ≤ \leq ≤)左右端点就可以了。
-
复杂度貌似是 O ( n l o g 2 ( r − l ) ) O(nlog_2(r-l)) O(nlog2(r−l))
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
void fff(){
freopen("linear.in","r",stdin);
freopen("linear.out","w",stdout);
}
const int N=1e5+10;
int n;
struct node{
int l,r;
bool operator <(const node x) const{
if(l==x.l) return r<x.r;
return l<x.l;
}
}a[N];
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x;
}
inline int get_fy(int x){
int s=0;
for(int i=1;i<=n;i++){
if(a[i].r<x) s+=(x-a[i].r);
if(a[i].l>x) s+=(a[i].l-x);
}
return s;
}
int ans1,ans2;
int main(){
fff();
n=read();
int ll=1e9,rr=0;
for(int i=1;i<=n;i++){
a[i].l=read(),a[i].r=read();
ll=min(a[i].l,ll);
rr=max(a[i].r,rr);
}
int l=ll,r=rr;
while(l<=r){
int mid=(l+r)>>1;
int t1=get_fy(mid-1),t2=get_fy(mid);
if(t1>t2)l=(ans1=mid)+1;
else r=mid-1;
}
l=ll,r=rr;
while(l<=r){
int mid=(l+r)>>1;
int t1=get_fy(mid),t2=get_fy(mid+1);
if(t2>t1) r=(ans2=mid)-1;
else l=mid+1;
}
cout<<ans1<<' '<<ans2;
}
第三题——模模塔
【题目描述】
- 给出数列 a [ 1.. n ] a[1..n] a[1..n]和 b [ 0.. n − 1 ] b[0..n-1] b[0..n−1],求出数列 c [ 1.. n ] c[1..n] c[1..n]
- 给出函数 c i = ∑ j = 1 i a ⌊ i j ⌋ b i   m o d   j c_i=\sum_{j=1}^{i}{a_{\lfloor \frac{i}{j} \rfloor}b_{i \, mod\,j} } ci=∑j=1ia⌊ji⌋bimodj
- 其实我这道题真的只会暴力orz,std给的玄学分块其实是把这个函数强行优化到 O ( n n ) O(n\sqrt n) O(nn),所以接下来可以慢慢来看这个表演…
- 考虑对于 ⌊ i j ⌋ \lfloor \frac{i}{j} \rfloor ⌊ji⌋ 进行分块,看的出来相同的 ⌊ i j ⌋ \lfloor \frac{i}{j} \rfloor ⌊ji⌋ 的 j j j是连续的那么b就是这些下表的等差数列orz…然后在 j ≤ i j\leq \sqrt i j≤i的范围,就可以直接进行暴力赋值,复杂度是 O ( n n ) O(n\sqrt n) O(nn)
- 然后对于 j > i j> \sqrt i j>i 的数据来说,预处理出 f [ i ] [ k ] f[i][k] f[i][k]表示的从 i i i开始的往前公差为 k k k的前缀和… O ( 1 ) O(1) O(1)查询。复杂度为 O ( n n ) O(n\sqrt n) O(nn)
- 所以总的复杂度还是 O ( n n ) O(n \sqrt n) O(nn),印证了那一句“世界上只有两种算法的话”
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
void fff(){
freopen("mmt.in","r",stdin);
freopen("mmt.out","w",stdout);
}
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x;
}
const int K=330;
const int N=100010;
const int MOD=123456789;
int n;
int a[N],b[N],c[N];
int sum[K][N];
int main(){
// fff();
n=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)b[i-1]=read();
for(int i=1;i<K;i++)
for(int j=0;j<n;j++){
if(j>=i) sum[i][j]=sum[i][j-i];
sum[i][j]=(sum[i][j]+b[j])%MOD;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=K&&j<=i;j++)
c[i]=(c[i]+(LL)a[i/j]*b[i%j])%MOD;
for(int l=K+1,r=K+1;l<=i;l=r+1){
int t=i/l;r=i/t;
c[i]=(c[i]+(LL)a[t]*(MOD+sum[t][i-t*l]-(i>t*(r+1)?sum[t][i-t*(r+1)]:0)))%MOD;
}
}
for(int i=1;i<=n;i++) printf("%d\n",c[i]);
}