前言
今天各种水分,就水到了50分,(dalao太强了)
题目
JZOJ 1494 密码
题目大意:高精度乘法
(题目数据范围:
1024
1024
1024,实际数据范围
1
0
24
10^{24}
1024,一堆WA了)
由于比较水,所以直接贴代码!(压8位)
代码
#include <cstdio>
#include <cctype>
#include <cstring>
using namespace std;
int len,n,x; long long a[301]; char s[27];
int min(int a,int b){return (a<b)?a:b;}
void times(){
long long c[302]; memset(c,0,sizeof(c));
len=strlen(s+1); long long k[3]={0,0,0}; int g=2;
while (len>0){
for (int t=1;t<=min(len,8);t++) k[g]=k[g]*10+s[len-min(len,8)+t]-48;
len-=8; g--;
}//压8位
for (int i=300;i>=1;i--)
for (int j=2;j>=0;j--)
c[i+j-1]+=a[i]*k[j];//高精乘
for (int i=300;i>=1;i--) a[i]=c[i+1]%100000000,c[i]+=c[i+1]/100000000;//把乘积返回第一个数列
}
int main(){
scanf("%d\n",&n);
gets(s+1); len=strlen(s+1);
for (int i=300;len>0;i--){
for (int j=min(len-1,7);j>=0;j--)//8位
a[i]=a[i]*10+s[len-j]-48;
len-=8;
}//预先处理第一个串
for (int i=1;i<n;i++) gets(s+1),times();
int k;
for (k=1;k<300&&!a[k];k++);
printf("%d",a[k++]);
while (k<=300){
if (a[k]<10) putchar('0');
if (a[k]<100) putchar('0');
if (a[k]<1000) putchar('0');
if (a[k]<10000) putchar('0');
if (a[k]<100000) putchar('0');
if (a[k]<1000000) putchar('0');
if (a[k]<10000000) putchar('0');
printf("%d",a[k++]);
}
return 0;
}
JZOJ 1495 宝石
PS:又把最难的放在第二题
题目大意
n颗宝石,各在第 x i x_i xi行,第 y i y_i yi列,用k*k的矩阵覆盖宝石,求最大覆盖的宝石的总价值。
分析
由于
n
<
=
50000
n<=50000
n<=50000所以前缀和等O(
n
2
n^2
n2)算法绝对是不可能的,所以
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)算法才是真正要运用的(
m
<
=
50000
m<=50000
m<=50000(PS:题目大意
1
≤
x
i
,
y
i
≤
m
1≤x_i,y_i≤m
1≤xi,yi≤m),在此可能把
m
m
m说成
n
n
n,在讲完算法后重新注明),可以按照列从小到大排序,可以建立一个区间的上下界,用O(n)扫描宝石(宝石是上界),下界通过宝石不断向上推
w
h
i
l
e
(
a
[
i
]
.
y
−
a
[
t
]
.
y
>
k
)
t
+
+
while (a[i].y-a[t].y>k) t++
while(a[i].y−a[t].y>k)t++;
i
i
i是当前的宝石(上界),
t
t
t是下界,但是这样做是远远不够的,把列完成了,行可以抽象成一个数轴(画图不好,请见谅)
对于任意一个点x,抽象后x的值可以扩散至
x
+
y
(
1
≤
y
≤
k
)
x+y(1≤y≤k)
x+y(1≤y≤k),那么x到x+k都应该累加x的价值,所以就是寻找公共部分,使公共部分最大,于是线段树就出来了,当然要用懒标记(节约时间),所以最终的算法时间复杂度是
O
(
n
l
o
g
m
)
O(nlogm)
O(nlogm)PS:下界增加时要处理线段树(取负数)
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#define us unsigned short
using namespace std;
struct site{us x,y;int w;}a[50001]; int f[200001],lazy[200001],ans; us t,n,m,k;
int in(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
int max(int a,int b){return (a>b)?a:b;}
int min(int a,int b){return (a<b)?a:b;}
void update(int k,int l,int r,int x,int y,int w){
if (l==x&&r==y) f[k]+=w,lazy[k]+=w;//找到区间
else{
if (lazy[k]){//懒标记
f[k<<1]+=lazy[k]; f[k<<1|1]+=lazy[k];
lazy[k<<1]+=lazy[k]; lazy[k<<1|1]+=lazy[k]; lazy[k]=0;
}
int mid=(l+r)>>1;
if (y<=mid) update(k<<1,l,mid,x,y,w);//区间在左边
else if (x>mid) update(k<<1|1,mid+1,r,x,y,w);//区间在右边
else update(k<<1,l,mid,x,mid,w),update(k<<1|1,mid+1,r,mid+1,y,w);//区间在中间
f[k]=max(f[k<<1],f[k<<1|1]);//计算最大值
}
}
bool cmp(site x,site y){return x.y<y.y;}
int main(){
m=in(); n=in(); k=in(); t=1;
for (int i=1;i<=n;i++) a[i].x=in(),a[i].y=in(),a[i].w=in();
stable_sort(a+1,a+1+n,cmp);
for (int i=1;i<=n;i++){
while (a[i].y-a[t].y>k) update(1,1,m,a[t].x,min(a[t].x+k,m),-a[t].w),t++;//处理下界(取负数)
update(1,1,m,a[i].x,min(a[i].x+k,m),a[i].w); ans=max(ans,f[1]);//加入线段树并输出答案
}
return !printf("%d",ans);
}
JZOJ 1496 页
题目大意
每次移动是将最中间的点放入最前面或最后面,求最少移动的次数(可能无解)
分析
可以用广搜的方法,不断找出新的状态,并用哈希表存储是否重复出现(map库也是可以的),当达到目标状态时统计移动次数即可。
代码
#include <cstdio>
#include <map>
#define p 282881//冒着风险开小点(正常情况是362881(9!=362880))
using namespace std;
int t,ans=2147483647,head,tail; long long h[p],k; short list[p],q[p][10],c,n,mid,a[10];
long long hash(){//求哈希值
long long ans=0;
for (int i=1;i<=n;i++) ans=(ans<<3)+(ans<<1)+a[i];
return ans;
}
int find(long long t){//找哈希表的位置
int y=t%p;
while (h[y]&&h[y]!=t) y=(++y)%p;
return y;
}
void push(long long y,long long t){
h[y]=t;
tail=tail%p+1;
for (int i=1;i<=n;i++) q[tail][i]=a[i];
}
bool check(){for (int i=1;i<n;i++) if (a[i]>a[i+1]) return 0; return 1;}//从小到大排列
void bfs(){
k=hash(); t=find(k); push(t,k);//哈希表
while (head!=tail){
head=head%p+1;//循环队列
for (int i=1;i<=n;i++) a[i]=q[head][i]; c=a[mid];//状态从队首开始(下同)
for (int i=mid;i>1;i--) a[i]=a[i-1]; a[1]=c;//移到最前面
k=hash(); t=find(k);
if (h[t]!=k) push(t,k),list[tail]=list[head]+1;//插入新的状态(下同)
if (check()) {ans=list[tail]; return;}//找到答案(下同)
for (int i=1;i<=n;i++) a[i]=q[head][i]; c=a[mid];
for (int i=mid;i<n;i++) a[i]=a[i+1]; a[n]=c;//移到最后面
k=hash(); t=find(k);
if (h[t]!=k) push(t,k),list[tail]=list[head]+1;
if (check()) {ans=list[tail]; return;}
}
return;
}
int main(){
scanf("%d",&n); mid=(n+1)/2;
for (int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]-=159;//便于储存
bfs();
if (ans==2147483647) return !printf("No Answer");
else return !printf("%d",ans);
}
JZOJ 1497 景点中心
题目大意:
有一棵树,每个节点有权值,每条边上是距离,找出一个节点,使其它节点到该节点时, ∑ \sum ∑节点权值*到该节点的距离最小。
分析
可以以任意一个节点为根节点,计算答案比较容易
用father表示该节点的上一个节点,td和tp分别表示到该节点的总距离和总人数,d和p是距离和人数
for (int i=ls[x];i;i=e[i].next){
if (e[i].y==father[x]) continue;//跑到上一个节点
father[e[i].y]=x; td[e[i].y]=e[i].w+td[x];
dfs(e[i].y); tp[x]+=tp[e[i].y];
}
tp[x]+=p[x];
t + = t d [ i ] ∗ p [ i ] , t t+=td[i]*p[i],t t+=td[i]∗p[i],t就是答案,之后不断移动根,二次扫描换根法,统计最小值比较简单
for (int i=ls[x];i;i=e[i].next){
if (e[i].y==father[x]) continue;//跑到上一个节点
long long tmp=t; //保留答案
t=t-tp[e[i].y]*e[i].w+(tp[1]-tp[e[i].y])*e[i].w;
dfs2(e[i].y); t=tmp;//用于下一次深搜
}
当把红框内的节点设为根节点变为黄框内的节点设为根节点时,红框的右子树会多走红框与黄框的距离,而黄框的左子树少走这个距离,中间的语句就是这样做的,讲完这些基本上没有什么难度了。
代码
#include <cstdio>
#include <cctype>
#define N 100001
using namespace std;
struct node{int y,w,next;}e[~-(N<<1)];
int d[N],p[N],father[N],k,n,ls[N],ans1,td[N],tp[N]; long long t,ans2=1ll<<62;
int in(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void add(int x,int y,int w){e[++k]=(node){y,w,ls[x]}; ls[x]=k;}
void dfs(int x){
for (int i=ls[x];i;i=e[i].next){
if (e[i].y==father[x]) continue;
father[e[i].y]=x; td[e[i].y]=1ll*e[i].w+td[x];
dfs(e[i].y); tp[x]+=tp[e[i].y];
}
tp[x]+=1ll*p[x];
}
void dfs2(int x){
if (ans2>t) ans2=t,ans1=x;
for (int i=ls[x];i;i=e[i].next){
if (e[i].y==father[x]) continue;
long long tmp=t;
t=t-1ll*tp[e[i].y]*e[i].w+1ll*(tp[1]-tp[e[i].y])*e[i].w;
dfs2(e[i].y); t=tmp;
}
}
int main(){
n=in(); for (int i=1;i<=n;i++) p[i]=in();
for (int i=1;i<n;i++){
int x=in(); int y=in(); int w=in();
add(x,y,w); add(y,x,w);
}
dfs(1);
for (int i=1;i<=n;i++) t+=td[i]*p[i];
dfs2(1);
return !printf("%d\n%lld",ans1,ans2);
}
后续
再接再厉,继续努力!