小康 发表于 2005-8-4 11:01

聊聊Contest - PKU 2005 ACM Team Exercise 2

http://acm.pku.edu.cn/JudgeOnline/showcontest?contest_id=1153

总共8题,按题目AC人数,我们一组人应该要做上5道题,目前我们做出了4道,
分别事A C F G 还差E

我简单说说A C F G 4题的思路,不对的地方请大家指正

F是我认为的,最简单的。

F的一题,就是求那个多项式的余数。
由于除数只有很简单的两个“单位”x^k+1 x没有系数。我们可以采用一种快速的谈心算法。从被除数最高位起计算到最低位,每次被除数的系数,就是商的系数,这样肯定相减为0,而我们只要计算1×商(注意系数也要),然后加回到被除数就好。这样最后剩下的被除数(k-1为到个位)就是余数

代码如下

#include<stdio.h>
int main()
{
        int num,i,j,k,n,m,u;
        freopen("in.txt","r",stdin);
        while(1)
        {
                scanf("%d %d",&n,&k);
                if(n==-1 && k==-1) break;
                for (i=0;i<=n;i++)
                {
                        scanf("%d",&num);
                }       
               
                for (i=n;i>=k;i--)
                {
                        u=i-k;//除数的1,相对应的被除数               
                        num=num-num;
                        num=0;

                }
                i=n;
                while(i && num==0) i--;                               
                {
                        printf("%d",num);
                        for (j=1;j<=i;j++)
                                printf(" %d",num);
                }
                i=0;
                printf("\n");

        }
        return 0;
}

小康 发表于 2005-8-4 11:05

C也不难,大家都做对了C。
C的做法是标号法
同一组,给一个组号。
如果两个组合并为同一组,就把任意一组的组号全部该为另外一组的。这里要注意些技巧,不好超时,因为最大的数字是n=5万,很可能会写成o(n*m)=o(n*n*(n-1)/2),的算法,
我到现在还没有A,不知道为什么,A的请贴贴

小康 发表于 2005-8-4 11:09

G也不难,但是做对的人不多。
如果范围不是1千万,是1百万或者更小,我们可以用标号法,不断覆盖数组上的值。
这道题是1千万,显然要有一个比较好的数据结构,我选用的是一个链表,其中为了加快删除结点速度,我用双向链表。
每贴一张海报,看看是否与前面的海报发生覆盖事件,大概有3种情况
覆盖了一张海报的左边或右边,修改下原来的海报的尺寸
覆盖了海报的中间,海报分为左边右边两部分,那么要新增一个结点,把原来的海报修改为海报的左边,新增海报右边部分到队尾。
完全覆盖了一张海报,删除原来海报
最后做一个统计
我的算法还是比较慢的,有更快的思路请帖出来共享一下

#include<stdio.h>
struct node
{
        int l,r,up,next,num;
} post;
int head,total,tail;
void add(int l,int r,int num)
{
        total++;
        post.l=l;
        post.r=r;
        post.num=num;
        post.up=tail;
        post.next=0;
        post.next=total;
        tail=total;
}
void del(int k)
{
        int up;
        if(k==head)
                head=post.next;
        else if(k==tail)
                tail=post.up;
        up=post.up;
        post.next=post.next;
        up=post.next;
        post.up=post.up;
}
int main()
{
        int i,j,k,n,m,num,a,b,l,r;
        head=1;
        freopen(\"in.txt\",\"r\",stdin);
        scanf(\"%d\",&n);
        while(n--)
        {
                scanf(\"%d\",&m);
                for (i=0;i<=m;i++) num=0;
                scanf(\"%d %d\",&a,&b);
                head=1;total=1;tail=1;
                post.up=0;post.next=0;
                post.l=a;post.r=b;
                post.num=1;
                num=1;
                for (i=1;i<m;i++)
                {
                        scanf(\"%d %d\",&a,&b);
                       
                        j=head;
                        k=tail;
                        add(a,b,i+1);
                        num++;
                        while(j<=k)
                        {                               
                                l=post.l;
                                r=post.r;
                               
                               
                                if(a<=l)
                                {
                                        if(b<r)
                                        {
                                                if(b>=post.l)
                                                        post.l=b+1;
                                        }else
                                        {               
                                                num.num]--;
                                                del(j);                                               
                                        }

                                }else if(a<=r)
                                {
                                        if(b>=r)
                                        {
                                                post.r=a-1;

                                        }else
                                        {
                                                add(b+1,r,post.num);
                                                num.num]++;
                        post.r=a-1;
                                        }
                                }

                                j=post.next;
                        }//while

                }//for
                k=0;               
                for (i=1;i<=m;i++) if(num>0) k++;
               
                printf(\"%d\\n\",k);               
        }
        return 0;
}

小康 发表于 2005-8-4 11:18

A题用搜索一点都不难,10分钟可以写一个超时的代码出来。
由于数据太大,我们要做一个优化,我用的是动态规划思想,把重复计算的部分保存下来。
我们看这样一组数据,m=20,n=5,我们会有这些序列
1 2 a1 a2 a3 (a1+a2+a3=17)
1 3 a1 a2 a3(=16)
4 4 a1 a2 a3(=12)
我们可以发现,对于a2 a3,会重复计算到 (a2=4 a5=x)
1 2 a1 4 a3
1 3 a1 4 a3
1 4 a1 4 a4
那么我们可以定义一个数组,
A 表示,两个数字的序列,当首位是f,总数是m,有多少种,
B 表示,三个数字的序列,当首位是f,总数是m,有多少种,
A和B存在一种关系
我们看看
假设m=20 ,我们要求 a1 a2 a3
那么 a1=1,2,3...(int)20/3
我们知道a1,那么,我们可以知道,B=A+A......
即, 看一下数列,我们要求1开头的序列的数量,可以发现 1 1 a2 ,1 2 a2,1,3 a2...
而1 a2,2 a2,3 a2 ,他们可以在数组A中求得。
这样有A,就可以求得B,那么,我们可以有B,得到C,接着得到D
我们可以定义一个这样的数组, NUM;
实际上,这个数组还可以压缩,因为求B后,A就不需要用到了
我们得到NUM后,可以简单的求出答案了。

小康 发表于 2005-8-4 11:19

#include<stdio.h>
int add;
int main()
{
        int i,j,k,a,b,c,cs,n,m,book,t,cases;
        freopen(\"in.txt\",\"r\",stdin);
        scanf(\"%d\",&cases);
        while(cases--)
        {
                scanf(\"%d %d %d\",&m,&n,&cs);               
                for (c=1;c<3;c++)
                for (i=0;i<=m;i++)
                        for (j=0;j<=m;j++)
                                add=0;
                for (i=1;i<=m;i++)
                {                       
                        add=1;
                       
                       
                }
                if(i>1)
                {
                        for (j=1;j<=m;j++)
                        {
                                k=1;
                                while(k+k<=j)
                                {
                                        add=1;
                                        k++;
                                }
                        }

                }
                for (i=3;i<=n;i++)
                {
                       
                        for(j=i;j<=m;j++)
                        {
                                for (k=1;k*i<=j;k++)
                                {
                                        add=0;
                                        a=k;
                                        while( j-k>=a*(i-1))
                                        {
                                                add+=add;
                                                a++;
                                        }
                                        a=k;
                                }
                        }
                }
                i=n;
                k=1;
                j=m;
                book=m;
                while(i>1)
                {                               
                       
                       
                        t=add;
                        while( t<cs && k*i<=j)
                        {                               
                                k++;                       
                                t+=add;
                        }
                        book=k;
                        book-=k;
                       
                                                                       

                                t-=add;
                                                                j-=k;
                                cs-=t;
                       
                        i--;
                       
                }
                i=n;
                for (i=n;i>0;i--)
                {
                        j=book;
                        printf(\"%d\\n\",j);
                }
               
               
        }

        return 0;

}

小康 发表于 2005-8-4 12:08

在大象同学的启发下,想出了E的做法,至此,我们完成了5道题,能完成5道题的帐号有32个,不过我们是超时的。
E中,如果输入的点数是1,那么很简单,是中心点是自己,如果点数是偶数,
我们把所有的点的,x,y坐标分别加起来,肯定为他中心点的n/2倍。
这个很容易证明。假设有n/2对,那么这些点肯定是不重复的,任意一对点的x坐标的和的1/2为中心点,那么把所有的点加起来,不就正好是n/2倍吗。

如果是奇数点,那么我们可以找到肯定有一个技术点为中心点,不然我们可以马上知道无解。然后加上这个点,当作偶数点进行处理。
接下来知道中心点的坐标,我们就可以很快搜索了。

我们找一个为匹配的点,搜寻其他未匹配的点,判断他们的和的n/2倍,是否为中心点的n/2倍,如果是,则寻找下一个点

代码如下

#include<stdio.h>
int x,y;

int main()
{
        int i,j,k,n,maxx,maxy,m,mx,my,f,book,mm;
        freopen(\"in.txt\",\"r\",stdin);
        i=10000000000;
        scanf(\"%d\",&n);
        while(n--)
        {
                scanf(\"%d\",&m);               
                maxx=maxy=0;
                for (i=0;i<m;i++)
                {
                        scanf(\"%d %d\",&x,&y);
                        maxx+=x;
                        maxy+=y;
                }

                if(n==1)
                {
                        printf(\"yes\\n\");
                        continue;
                }
               
                f=0;
                mm=m/2;
                for (i=0;i<m;i++) book=0;
                if((m&1)==1)
                {
                        for (i=0;i<m;i++) if(x*m==maxx && y*m==maxy)
                                break;
                        book=1;
                        if(i==m)
                        {
                                printf(\"no\\n\");
                                continue;
                        }
                        maxx-=x;
                        maxy-=y;

                }

                for (i=0;i<m;i++) if(book==0)
                {
                        for (j=i+1;j<m;j++) if(book==0)
                        {
                                k=x+x;
                                if(k*mm!=maxx) continue;
                                k=y+y;
                                if(k*mm!=maxy) continue;
                                book=1;
                                book=1;
                                break;
                        }
                        if(j==m) break;
                       
                }
                if(i==m)
                        printf(\"yes\\n\");
                else
                        printf(\"no\\n\");
               
        }
        return 0;


}
页: [1]
查看完整版本: 聊聊Contest - PKU 2005 ACM Team Exercise 2