2019.09.21 多校联合训练(提高组)

东方记者

一、题目

【题目描述】
这一天,在幻想乡发生了N起大新闻,第i起大新闻发生在坐标(Xi,Yi)处。射命丸文从坐标(0,0)处出发,按照新闻发生的时间顺序前往各个新闻发生地收集资料,最后回到坐标(0,0)处。虽然她的速度很快,但是她只会横冲直撞,换句话说,她的移动必须平行于某条坐标轴。而且她的力量是有限的,她移动的总距离不能超过D。所以她不得不放弃一些大新闻的资料收集。请问她最多可以收集多少起大新闻的资料?

二、解法

考虑 d p dp dp,定义 d p [ i ] [ j ] dp[i][j] dp[i][j]为到了 i i i个点收集 j j j起新闻所需要的最小路程,可以把整个转移理解为求一个最短路,直接跑 d i j k s t r a dijkstra dijkstra即可,时间复杂度 O ( n 2 l o g n ) O(n^{2}logn) O(n2logn)

#include <cstdio>
#include <queue>
#include <cstring>
#define int long long
using namespace std;
int read()
{
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,d,ans,dp[105][105],x[105],y[105];
struct node
{
	int u,step,dis;
	bool operator < (const node &x) const {
		return dis>x.dis;
	}
};
int abs(int x)
{
	return x>0?x:-x;
}
int mht(int x1,int y1,int x2,int y2)
{
	return abs(x2-x1)+abs(y2-y1); 
}
void dijkstra()
{
	priority_queue<node> q;
	memset(dp,0x3f,sizeof dp);
	for(int i=1;i<=n;i++)
		q.push(node{i,0,mht(0,0,x[i],y[i])});
	while(!q.empty())
	{
		node t=q.top();
		q.pop();
		for(int i=t.u+1;i<=n+1;i++)
		{
			int c=mht(x[t.u],y[t.u],x[i],y[i]);
			if(dp[i][t.step+1]>t.dis+c && t.dis+c<=d)
			{
				dp[i][t.step+1]=t.dis+c;
				q.push(node{i,t.step+1,t.dis+c}); 
			}
		}
	}
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
		x[i]=read(),y[i]=read();
	d=read();
	dijkstra();
	for(int i=n;i>=1;i--)
		if(dp[n+1][i]<=d)
			return !printf("%d\n",i);
	printf("0\n");
}

琪露诺数

一、题目

【题目描述】
对于一个十进制数X,它在九进制下表示为Y,如果Y是一个回文数字,那么我们称X是一个琪露诺数。请你找出[L,R]中有多少个琪露诺数。

二、解法

考试的时候想了个大概,但是高精(难上加难)。
我们考虑差分,求 f ( 1 , r ) − f ( 1 , l − 1 ) f(1,r)-f(1,l-1) f(1,r)f(1,l1)的值。问题转化成了求 f ( 1 , x ) f(1,x) f(1,x)的值,我们先把 x x x转化成 9 9 9进制,发现构造一个回文串只需要构造他的前半部分,发现回文数可以用前半部分的 10 10 10进制值表示,但是还要修正这个答案,把他加上与他异奇偶的回文串个数(直接预处理),如果前半部分比后半部分的值大,那么是构造不出来所有回文串的,要把前半部分给个 1 1 1给后半部分,等于将总个数减 1 1 1
思路其实不难,主要靠码力。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
struct Bignum
{
	int len,a[205];
	Bignum() {len=1;memset(a,0,sizeof a);}
	Bignum(int x)
	{
		len=0;memset(a,0,sizeof a);
		while(x)
		{
			a[++len]=x%10;x/=10;
		}
		len+=!len;
	}
	void read()
	{
		char s[105];scanf("%s",s+1);
		len=strlen(s+1);
		for(int i=len;i>=1;i--) a[i]=s[len-i+1]^48;
	}
	void print()
	{
		for(int i=len;i>=1;i--) printf("%d",a[i]);
		puts("");
	}
	void p()
	{
		for(int i=1;i<=len;i++) printf("%d",a[i]);
		puts("");
	}
	Bignum reverse()
	{
		Bignum ret=*this;
		for(int i=1;(i<<1)<=len;++i) swap(ret.a[i],ret.a[len-i+1]);
		while(ret.len>1 && !ret.a[ret.len] && --ret.len);
		return ret;
	}
	bool zero()
	{
		return len==1 && !a[1];
	}
	bool operator <= (Bignum x) const
	{
		if(len^x.len) return len<x.len;
		for(int i=len;i>=1;i--)
			if(a[i]^x.a[i])
				return a[i]<x.a[i];
		return 1;
	}
	Bignum operator + (Bignum x) const
	{
		Bignum ret(0);
		ret.len=max(len,x.len)+1;
		for(int i=1;i<=ret.len;i++)
		{
			ret.a[i]+=a[i]+x.a[i];
			ret.a[i+1]+=ret.a[i]/10;
			ret.a[i]%=10;
		}
		while(ret.len>1 && !ret.a[ret.len] && --ret.len);
		return ret;
	}
	Bignum operator - (Bignum x) const
	{
		Bignum ret(0);
		for(int i=1;i<=len;i++)
		{
			ret.a[i]+=a[i]-x.a[i];
			if(ret.a[i]<0)
			{
				ret.a[i]+=10;
				ret.a[i+1]--;
			}
		}
		ret.len=len;
		while(ret.len>1 && !ret.a[ret.len] && --ret.len);
		return ret;
	}
	Bignum operator * (Bignum x) const
	{
		Bignum ret(0);
		for(int i=1;i<=len;i++)
		{
			for(int j=1;j<=x.len;j++)
			{
				ret.a[i+j-1]+=a[i]*x.a[j];
				ret.a[i+j]+=ret.a[i+j-1]/10; 
				ret.a[i+j-1]%=10;
			}
		}
		ret.len=len+x.len;
		while(ret.len>1 && !ret.a[ret.len] && --ret.len);
		return ret;
	}
	Bignum operator / (int x) const 
	{
		Bignum ret(0);int v=0;
		for(int i=len;i>=1;i--)
		{
			ret.a[i]=(a[i]+v*10)/x;
			v=(a[i]+v*10)%x;
		}
		ret.len=len;
		while(ret.len>1 && !ret.a[ret.len] && --ret.len);
		return ret;
	}
	int Mod_9()
	{
		int ret=0;
		for(int i=1;i<=len;i++) ret=(ret+a[i])%9;
		return ret; 
	}
	Bignum turn_9()
	{
		Bignum tmp=*this,ret(0);
		ret.len=0;
		while(!tmp.zero())
		{
			ret.a[++ret.len]=tmp.Mod_9();
			tmp=tmp/9;
		}
		while(ret.len>1 && !ret.a[ret.len] && --ret.len);
		ret.len+=!ret.len;
		return ret;
	}
	Bignum turn_10()
	{
		Bignum ret(0);
		for(int i=len;i>=1;i--)
		{
			ret=ret*9+a[i];
		}
		return ret;
	}
}L,R,ten(10),rev[115]={1,9},sum[115]={0,8};
void prepare()
{
	for(int i=2;i<=110;i++)
	{
		rev[i]=rev[i-2]*Bignum(9);
		sum[i]=sum[i-2]+rev[i]-rev[i-2];
	}
}
Bignum cal(Bignum x)
{
	if(x.zero()) return Bignum(0);
	Bignum fr(0),ba(0);int l=(x.len>>1);
	for(int i=x.len;i>l;i--)
	{
		fr=fr*ten+Bignum(x.a[i]),ba=ba*ten+Bignum(x.a[i-l]);
	}
	return fr.turn_10()-!(fr.reverse()<=ba)+sum[x.len-1];
}
int main()
{
	prepare();
	int T;scanf("%d",&T);
	while(T--)
	{
		L.read();R.read();L=L-1;
		L=L.turn_9();R=R.turn_9();
		(cal(R)-cal(L)).print();
	}
}//21 20 12 17 18

御神渡

一、题目

【题目描述】
幻想乡有N个区域,从1到N编号,神奈子希望通过修建“御神渡”来让所有的区域互相可达,“御神渡”可以看做两个区域之间的一个双向道路。在i区域与j区域之间修建“御神渡”,需要在i区域耗费Ci的神力新建神社,在j区域耗费Cj的神力新建神社,然后将两个神社配对。如果一个区域和其他多个区域建立“御神渡”,那么在这个区域要为每个“御神渡”建一个神社,即多个“御神渡”不能共用神社。

“御神渡”会促进两个区域的人口流动,而人口的流动就会带来文化的交流、信仰的增多。如果i区域的人口为Ai,j区域有的人口为Aj,则神奈子将会得到Ai*Aj的神力。

神奈子希望修建尽量少的“御神渡”,使得n个区域互相可达。在这个前提下,神奈子还希望自己总神力的减少量最小(注意该值可能为负,即神奈子的神力不减反增)。

输入格式
第一行一个非负整数N,表示区域的数量。

第二行N个非负整数Ai,分别表示N个区域的人口。数据保证每个区域的人口互不相等。

第三行N个非负整数Ci,分别表示在N个区域建神社要花费的神力。

输出格式
输出一个整数,表示神奈子总神力的减少量的最小值。

二、解法

不会。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值