2013 Multi-University Training Contest 3

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;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值