2018.07.07【2018提高组】模拟C组

111 篇文章 0 订阅
76 篇文章 0 订阅

前言

今天各种水分,就水到了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 &lt; = 50000 n&lt;=50000 n<=50000所以前缀和等O( n 2 n^2 n2)算法绝对是不可能的,所以 O ( n l o g n ) O(nlogn) Onlogn算法才是真正要运用的( m &lt; = 50000 m&lt;=50000 m<=50000(PS:题目大意 1 ≤ x i , y i ≤ m 1≤x_i,y_i≤m 1xi,yim),在此可能把 m m m说成 n n n,在讲完算法后重新注明),可以按照列从小到大排序,可以建立一个区间的上下界,用O(n)扫描宝石(宝石是上界),下界通过宝石不断向上推 w h i l e ( a [ i ] . y − a [ t ] . y &gt; k ) t + + while (a[i].y-a[t].y&gt;k) t++ while(a[i].ya[t].y>k)t++ i i i是当前的宝石(上界), t t t是下界,但是这样做是远远不够的,把列完成了,行可以抽象成一个数轴(画图不好,请见谅)
这里写图片描述
对于任意一个点x,抽象后x的值可以扩散至 x + y ( 1 ≤ y ≤ k ) x+y(1≤y≤k) x+y1yk,那么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);
}

后续

再接再厉,继续努力!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值