1007 The Unsolvable Problem
这道题目的解题报告写错了。(/ □ \)。这道题的意思是给你一个数n,让后让你求出两个数字a,b使得a+b==n && a和b的最小公倍数最大。我们可以分情况来考虑。假设n是奇数(n==2*k+1),那么我们就可以把其分为k和k+1,这样就得到了此时的最小公倍数的最大值。当n==2*k时。我们还是分情况,假设k%2==1,比如26==13*2 && 13%2==1.那么我们可以发现对于12和14,以及11和15求奇数的最小公倍数是相对较大的。所以此时取k-2和k+2;对应的就是k%2==0.这时候比如28==14*2 && 14%2==0.那么对于13和15就是邻接14的奇数,直接k-1和k+1就可以了。当然,2直接特判就行,就是这样了。(/ □ \),代码:
#include <iostream>
#include <cstdio>
#include <string>
#include <string.h>
#include <map>
#include <vector>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <stack>
using namespace std;
int main()
{
int t;
__int64 n;
scanf("%d", &t);
while(t--)
{
scanf("%I64d", &n);
if(n==2)
{
cout<<1<<endl;
continue;
}
if(n%2==1)
cout<<(n/2)*(n/2+1)<<endl;
else if((n/2)%2==0)
cout<<(n/2-1)*(n/2+1)<<endl;
else
cout<<(n/2-2)*(n/2+2)<<endl;
}
return 0;
}
1010 No Pain No Game
这道题目在比赛的时候没有看,赛后看的时候发现又是这种卡时间的题目。题目很容易懂,就是给你一个数组,然后有X组询问。每组给你l,r两个区间,求出这个区间内最大的公约数。
木有算法就是水,想到一次次的遍历,立马就否决了。这就又是一个树状数组的应用。如果我们直接就对数组中的元素进行GCD运算。那么当每次求值时候,我们都需要遍历大量的数据,并作n多次gcd运算。对于这些询问,我们可以以询问的r点由小到大排序,这样按照其它的讲的,进行离线计算,排序之后,当我们的i值等于询问的r时,就计算前面的gcd,在更新一下,直到最后一个询问,树状数组掌握的太挫了:
#include <iostream>
#include <cstdio>
#include <string>
#include <string.h>
#include <map>
#include <vector>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <queue>
#include <set>
#include <stack>
using namespace std;
int a[50005];
int d[50005][150];
int t,n,m,x,y,u,k;
int pre[50005];
int ans[50005];
struct Query//记录询问区间和询问次序,最后输出时要注意次序
{
int l,r;
int id;
} q[50005];
int c[50005];
void init()
{
memset(d,0,sizeof(d));
for(int i = 1; i <50003; i++)
for(int j = i; j <50003; j+=i)
d[j][++d[j][0]]=i;
}
bool cmp(Query q1,Query q2)//按照询问的区间排序
{
return q1.r<q2.r;
}
void update(int st,int val)
{
for(int i=st; i<=n; i+=(i&(-i)))//lowbit()
c[i]=max(c[i],val);
}
int query(int lt)
{
int s=0;
for(int i=lt; i>0; i-=(i&(-i)))//lowbit()
s=max(s,c[i]);
return s;
}
int main()
{
init();
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=1; i<=m; i++)
{
scanf("%d%d",&x,&y);
q[i].l=x;
q[i].r=y;
q[i].id=i;
}
sort(q+1,q+m+1,cmp);//按照询问区间的顺序排序
memset(c,0,sizeof(c));
memset(pre,0,sizeof(pre));
k=1;
for(int i=1; i<=n; i++)
{
for(int j=1; j <= d[a[i]][0]; j++)
{
u = d[a[i]][j];
if(pre[u]!=0)
update(n-pre[u]+1, u);
pre[u]=i;
}
while(q[k].r==i&&k<=m)
{
ans[q[k].id]=query(n-q[k].l+1);
k++;
}
}
for(int i=1; i<=m; i++)
cout<<ans[i]<<endl;
}
return 0;
}
1011 Sad Love Story
这道题目就是一个最近点对的问题,要求每次加进来一个点时就求一次最近点对的距离的平方,最后把这些求和。刚开始的时候一直在按照题意直接写的,每来一个点就求一次最近点对。可是就算是20秒的时间也是TLE。后来听了一种解法,就是先对所有点求一次最近点对,比如说是i,j那么j以后的点加进来之后的最近距离就是i,j了。这样的话,我们对于后面的点就直接乘以i,j的距离就行了。就这样,继续对前面的点进行类似的操作。直到求出的最近点对的j点是第二个点就OK了,不过RE的,说是爆栈。再改,代码:
#include <iostream>
#include <cstdio>
#include <string>
#include <string.h>
#include <map>
#include <vector>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cmath>
#include <queue>
#include <set>
#include <stack>
using namespace std;
// 分治算法求最近点对
int st;
int ed;
struct point
{
__int64 x , y;
} p[500005];
struct node
{
__int64 x, y;
int fxy;
} pp[500005];
__int64 a[500005]; //保存筛选的坐标点的索引
__int64 cmpx(const node &a , const node &b)
{
return a.x < b.x;
}
__int64 cmpy(__int64 a , __int64 b) //这里用的是下标索引
{
return p[a].y < p[b].y;
}
inline __int64 dis(node &a , node &b)
{
return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y);
}
inline __int64 min(__int64 a , __int64 b)
{
return a < b ? a : b;
}
__int64 closest(__int64 low , __int64 high)
{
__int64 mi;
if(low + 1 == high)
{
st = pp[low].fxy;
ed = pp[high].fxy;
return dis(pp[low] , pp[high]);
}
if(low + 2 == high)
{
st = pp[low+1].fxy;
ed = pp[high].fxy;
mi = dis(pp[low+1] , pp[high]);
if(dis(pp[low] , pp[low+1]) < mi )
{
st = pp[low].fxy;
ed = pp[low+1].fxy;
mi = dis(pp[low] , pp[low+1]);
}
if(dis(pp[low] , pp[high]) < mi)
{
st = pp[low].fxy;
ed = pp[high].fxy;
mi = dis(pp[low] , pp[high]);
}
return mi;
}
__int64 mid = (low + high)>>1;//求中点
__int64 ans = min( closest(low , mid) , closest(mid + 1 , high) ); //分治法进行递归求解
__int64 i , j , cnt = 0;
for(i = low ; i <= high ; ++i) //把x坐标在p[mid].x-ans~p[mid].x+ans范围内的点取出来
{
if(pp[i].x >= pp[mid].x - ans && pp[i].x <= pp[mid].x + ans)
a[cnt++] = i; //保存的是下标索引
}
sort(a,a + cnt,cmpy); //按y坐标进行升序排序
for(i = 0 ; i < cnt ; ++i)
{
for(j = i+1 ; j < cnt ; ++j)
{
if(pp[a[j]].y - pp[a[i]].y >= ans) //注意下标索引
break;
if(ans > dis(pp[a[i]] , pp[a[j]]))
{
ans = dis(pp[a[i]] , pp[a[j]]);
st = pp[a[i]].fxy;
ed = pp[a[j]].fxy;
if(st > ed)
swap(st, ed);
}
}
}
return ans;
}
int main()
{
__int64 t;
scanf("%I64d", &t);
__int64 n, ax, bx,cx,ay,by,cy;
__int64 ans = 0;
while(t--)
{
ans = 0;
scanf("%I64d", &n);
scanf("%I64d%I64d%I64d%I64d%I64d%I64d", &ax,&bx,&cx,&ay,&by,&cy);
for(int i = 0; i < n; ++i)
{
if(i==0)
{
p[i].x = (0*ax+bx)%cx;
p[i].y = (0*ay+by)%cy;
}
else
{
p[i].x = (p[i-1].x*ax+bx)%cx;
p[i].y = (p[i-1].y*ay+by)%cy;
}
}
for(int i = 0; i < n; ++i)
{
pp[i].x = p[i].x;
pp[i].y = p[i].y;
pp[i].fxy = i;
}
sort(pp , pp+n , cmpx);
ans += closest(0 , n-1)*(n-1-ed+1);
while(ed != 1)
{
__int64 tp = ed-1;
st = 0;
ed = 0;
for(int i = 0; i <= tp; ++i)
{
pp[i].x = p[i].x;
pp[i].y = p[i].y;
pp[i].fxy = i;
}
sort(pp , pp+tp+1 , cmpx);
ans += closest(0 , tp)*(tp-ed+1);
}
printf("%I64d\n", ans);
}
return 0;
}
改了之后,又超时了,我觉得整体思路是对的,写的主要是那个while循环,我觉得是这里的问题啊:
#include <iostream>
#include <cstdio>
#include <string>
#include <string.h>
#include <map>
#include <vector>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cmath>
#include <queue>
#include <set>
#include <stack>
using namespace std;
inline void RD(int &ret)
{
char c;
do
{
c=getchar();
}
while(c<'0'||c>'9');
ret=c-'0';
while((c=getchar())>='0'&&c<='9')
{
ret=ret*10+(c-'0');
}
}
// 分治算法求最近点对
int st;
int ed;
struct point
{
__int64 x , y;
int xy;
} p[500005];
__int64 a[500005]; //保存筛选的坐标点的索引
inline __int64 cmpx(const point &a , const point &b)
{
return a.x < b.x;
}
inline __int64 cmpy(__int64 a , __int64 b) //这里用的是下标索引
{
return p[a].y < p[b].y;
}
inline __int64 dis(point &a , point &b)
{
return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y);
}
inline __int64 closest(__int64 low , __int64 high)
{
__int64 mi;
if(low + 1 == high)
{
st = p[low].xy;
ed = p[high].xy;
if(st > ed)
swap(st, ed);
return dis(p[low] , p[high]);
}
if(low + 2 == high)
{
st = p[low+1].xy;
ed = p[high].xy;
mi = dis(p[low+1] , p[high]);
if(dis(p[low] , p[low+1]) < mi )
{
st = p[low].xy;
ed = p[low+1].xy;
mi = dis(p[low] , p[low+1]);
}
if(dis(p[low] , p[high]) < mi)
{
st = p[low].xy;
ed = p[high].xy;
mi = dis(p[low] , p[high]);
}
return mi;
}
__int64 mid = (low + high)>>1;//求中点
__int64 zans = closest(low, mid);
int zx = st;
int zy = ed;
__int64 yans = closest(mid+1, high);
int yx = st;
int yy = ed;
__int64 ans;
if(zans < yans)
{
ans = zans;
st = zx;
ed = zy;
}
else
{
ans = yans;
st = yx;
st = yy;
}
__int64 i , j , cnt = 0;
for(i = low ; i <= high ; ++i) //把x坐标在p[mid].x-ans~p[mid].x+ans范围内的点取出来
{
if(p[i].x >= p[mid].x - ans && p[i].x <= p[mid].x + ans)
a[cnt++] = i; //保存的是下标索引
}
sort(a,a + cnt,cmpy); //按y坐标进行升序排序
for(i = 0 ; i < cnt ; ++i)
{
for(j = i+1 ; j < cnt ; ++j)
{
if(p[a[j]].y - p[a[i]].y >= ans) //注意下标索引
break;
if(ans > dis(p[a[i]] , p[a[j]]))
{
ans = dis(p[a[i]] , p[a[j]]);
st = p[a[i]].xy;
ed = p[a[j]].xy;
if(st > ed)
swap(st, ed);
}
}
}
return ans;
}
int main()
{
int t;
RD(t);
__int64 n, ax, bx,cx,ay,by,cy;
__int64 ans = 0;
while(t--)
{
ans = 0;
scanf("%I64d", &n);
scanf("%I64d%I64d%I64d%I64d%I64d%I64d", &ax,&bx,&cx,&ay,&by,&cy);
p[0].x = bx%cx;
p[0].y = by%cy;
p[0].xy = 0;
for(int i = 0; i < n; ++i)
{
p[i].x = (p[i-1].x*ax+bx)%cx;
p[i].y = (p[i-1].y*ay+by)%cy;
p[i].xy = i;
}
ans += closest(0 , n-1)*(n-1-ed+1);
while(ed > 1)
{
__int64 tp = ed-1;
st = 0;
ed = 0;
ans += closest(0 , tp)*(tp-ed+1);
}
printf("%I64d\n", ans);
}
return 0;
}