第一题
Description
有n堆石子,从1~n编号,其石子总数为2^k。
每次可以选择两堆石子a和b,满足a堆的石子数不比b堆的多,记c为a的石子数。然后可以进行以下操作:从b堆石子中拿c这么多的石子到a堆中。
要求你给出一个方案,使得最后有一堆石子的数目达到2^k。
Input
第一行两个正整数n,k。
第二行n个非负数ai。
Output
输出若干行,每行两个数a,b,表示每次操作中的两堆石子的编号,必须满足题中所给的大小关系!
Sample Input
2 2
3 1
Sample Output
2 1
1 2
Hint
【数据规模】 对于30%的数据,n=2;
对于100%的数据,n<=100000,k<=31。
【注释】 此题需要Special Judge。
思路:暴力
要使得最终得到2^k
转成二进制就是只有第一个1,其余都为0
那么就可以利用这个性质,从后向前依次将所有数的最后一位变0(因为当枚举到前面,后面怎么加都是0)
代码:
#include
#include
using namespace std;
int n,k,l;
long a[100000];
int main()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++) scanf("%ld",&a[i]);
l=0;
for (int i=1;i<=k;i++)
for (int j=n;j>=1;j--)
if ((a[j]&(1<<(i-1)))!=0)
if (l==0) l=j;
else
{
if (a[j]>a[l])
{
a[j]-=a[l];
a[l]=a[l]<<1;
printf("%d %d\n",l,j);
l=0;
}
else
{
a[l]-=a[j];
a[j]=a[j]<<1;
printf("%d %d\n",j,l);
l=0;
}
}
return 0;
}
第二题
Description
编号为1~n的n个城市,每个城市有两个权值Ai和Bi。
对于两个城市i和j,i可到j当且仅当j>i,而费用为(j-i)*Ai+Bj。
求从城市1到城市n的最小费用。
Input
第一行一个正整数n。
第二行n个正整数,第i个表示Ai。
第三行n个正整数,第i个表示Bi。
Output
一个数,表示最小的费用。
Sample Input
4
2 9 5 4
9 1 2 2
Sample Output
8
Hint
【数据规模】
对于20%的数据,1<=n<=100;
对于50%的数据,1<=n<=3000;
对于100%的数据,1<=n<=100000,1<=Ai,Bi<=10^9。
思路:
①DP+队列
更新i的状态可以是1~i-1,但是有很多状态是没有用的(永远小),所以弄一个队列存下可能有用的状态
队列:若有两个点j和k(满足j< k),若要k比j优,那么k->i <= j->i,且a[k]<=a[j],那就可以把k扔掉。每次更新完f[i]就把它加入队尾,然后维护队列。
②斜率优化+二分
首先,知道f[i]=min(f[j]+(j-i)*a[i]+b[j]),这应该每个人都知道
=min(f[j]+b[j]+j*a[i])-i*a[i]
发现,只有j*a[i]项存在两个不同的值(i和j)
那么类似于这种情况的都可以用斜率优化。
设g[j]=f[j]+b[j]
那么原式=min(g[j]+j*a[i])-i*a[i]
运用方法①的思路,要求出最优的点,如果枚举两个位置j和k,让j,k< i,j< k
那么如果g[j]+j*a[i]< g[k]+k*a[i]
=(j-k)*a[i]< g[k]-g[j]
=-a[i]<(g[k]-g[j])/(k-j)
然后我们就可以构造出一个单调上升的凸多边形的下壳,那么现在就可以去二分求出一个最优的点,如果当前求出的斜率(g[k]-g[j])/(k-j)>=-a[i],就将r左移,不然就右移l
在求出一个最优点,再存起来就行了
方法①代码:
#include
#include
using namespace std;
long long a[100000],b[100000],f[100000],k[100000];
int n,m,i,j,l,x;
long long much(int x,int y)
{
return f[x]+a[x]*(y-x);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
for (int i=1;i<=n;i++) scanf("%lld",&b[i]);
l=1;
for (int i=2;i<=n-1;i++)
{
if (k[i]!=0&&a[k[i]]if (much(l,i)>=much(k[i],i)) l=k[i];
else
{
x=(much(k[i],i)-much(l,i))/(a[l]-a[k[i]])+i+1;
if (x<=n&&(k[x]==0||a[k[i]]if (a[i]1;
if (x<=n&&(k[x]==0||a[i]if (k[n]>0&&a[k[n]]much(k[n],n)) l=k[n];
printf("%lld",much(l,n)+b[n]);
}
方法②代码:
#include
#include
using namespace std;
int n,head,tail,mid,l,r,j;
long long a[100000],b[100000],f[100000],k[100000],g[100000];
float xl(int x,int y)
{
return (g[x]-g[y])/(x-y);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
for (int i=1;i<=n;i++) scanf("%lld",&b[i]);
head=1; tail=1;
k[1]=n; f[n]=0; g[n]=f[n]+b[n];
for (int i=n-1;i>=1;i--)
{
if (headwhile (l1)
{
mid=(l+r)/2;
if (xl(k[mid+1],k[mid])<=-a[i]) r=mid; else l=mid;
}
if (xl(k[r],k[l])<=-a[i]) j=k[l]; else j=k[r];
}
else j=k[head];
f[i]=g[j]+j*a[i]-i*a[i];
g[i]=f[i]+b[i];
if (headwhile (xl(i,k[tail])>xl(k[tail],k[tail-1]))
{
tail--;
if (tail==head) break;
}
}
tail++;
k[tail]=i;
}
printf("%lld",f[1]);
}
第三题
Description
有一个外星人控制了你的大脑。一开始你处于原点(0,0)。外星人有一个由(R,U,D,L)组成的长度为M 的操作序列,分别代表(右,上,下,左)。
平面上有N 个关键点,每当外星人给出一个操作,你需要在这个方向上找到最近的一个关键点,并走到那个点上。保证输入数据合法。
上图为第三个样例的图示。
Input
第一行两个整数N,M。
接下来N 行,每行两个整数xi,yi,代表第i 个点的坐标。
接下来一行,一个长度为M 的字符串,代表操作序列。
Output
一行两个整数,代表最终你所处的位置。
Sample Input
输入1:
4 4
1 1
1 0
0 1
0 0
RULD
输入2:
7 5
0 0
0 1
0 -1
1 0
1 -1
3 0
3 -1
DRRUD
输入3:
10 6
0 0
1 1
2 1
0 2
-1 2
-1 3
2 3
2 4
4 3
2 -1
ULURDL
Sample Output
输出1:
0 0
输出2:
3 -1
输出3:
1 1
Data Constraint
56%的数据,N≤3000,M≤3000。
100%的数据,N,M≤100000,xi,yi≤200000。
思路:二分+排序
先将坐标排序,一个数组存横坐标小到大排,一个数组存纵坐标小到大排
将每一个寻找,用二分实现
代码:
#include
#include
#include
#include
using namespace std;
struct cf{
int x,y;}a[100010],b[100010],tmp1,tmp2;
bool cmp1(cf a,cf b){
if (a.yreturn true; else return false;}
bool cmp2(cf a,cf b){
if (a.xreturn true; else return false;}
int lx,ly,n,m;
char c;
int efl(cf s)
{
int l=1,r=n;
while (lint mid=(l+r)>>1;
if (b[mid].x1; else r=mid;
}
return l;
}
int efh(cf s)
{
int l=1,r=n;
while (lint mid=(l+r)>>1;
if (a[mid].y1; else r=mid;
}
return l;
}
int main()
{
freopen("tratincice.in","r",stdin);
freopen("tratincice.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d%d",&lx,&ly);
a[i].x=b[i].x=lx;
a[i].y=b[i].y=ly;
}
sort(a+1,a+n+1,cmp1);
sort(b+1,b+n+1,cmp2);
for (int i=1;i<=m;i++)
{
while ((c=getchar())!='D'&&c!='R'&&c!='U'&&c!='L');
if (c=='U')
{
int w=efl(tmp2)+1;
tmp1.x=b[w].x;tmp1.y=b[w].y;
tmp2.x=b[w].x;tmp2.y=b[w].y;
}
if (c=='D')
{
int w=efl(tmp2)-1;
tmp1.x=b[w].x;tmp1.y=b[w].y;
tmp2.x=b[w].x;tmp2.y=b[w].y;
}
if (c=='L')
{
int w=efh(tmp1)-1;
tmp1.x=a[w].x;tmp1.y=a[w].y;
tmp2.x=a[w].x;tmp2.y=a[w].y;
}
if (c=='R')
{
int w=efh(tmp1)+1;
tmp1.x=a[w].x;tmp1.y=a[w].y;
tmp2.x=a[w].x;tmp2.y=a[w].y;
}
}
printf("%d %d",tmp1.x,tmp1.y);
}