东方记者
一、题目
【题目描述】
这一天,在幻想乡发生了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,l−1)的值。问题转化成了求
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个区域建神社要花费的神力。
输出格式
输出一个整数,表示神奈子总神力的减少量的最小值。
二、解法
不会。。