HDU 6127 Hard challenge+HDU 6129 Just do it【2017多校联赛】

来源:互联网 发布:逐浪cms 编辑:程序博客网 时间:2024/06/06 19:50

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6127


官方题解:平面直角坐标系上有n个整点,第i个点有一个点权val_i,坐标为(x​i​,y​i​​),其中不存在任意两点连成的直线经过原点。这些整点两两之间连有一条线段,线段的权值为其两端点的权值之积。你需要作一条过原点而不过任意一个给定整点的直线,使得和这条直线相交的线段的权值和最大。

对于一条直线,线段权值和实际上就等于其两边点权和的乘积,所以把所有点按极角排个序,然后扫一圈就好了。


分析:对所有的点先极角排序,以Y轴为分界线开始扫描,分别计算直线左右两边的价值和,则score=lsum*rsum,然后对点扫描(其实就是按顺时针方向旋转得到新的直线),更新lsum,rsum(根据具体的图来更新,lsum+,rsum-/sum+,lsum-),更新最大score。【注意数据范围】

CODE:

#include<stdio.h>#include<string.h>#include<algorithm>#include<queue>#include<stack>#include<math.h>#include<map>#include<iostream>#define INF 0x3f3f3f3f#define lson l,m,root<<1#define rson m+1,r,root<<1|1#define PI acos(-1.0)typedef  long long LL;using namespace std;const int mod=998244353;const int maxn=3000005;struct node{    double x,y,k;    LL val;    bool operator < (const node &cmp)const    {        return k<cmp.k;    }}c[maxn];int main(){   int t,n;   scanf("%d",&t);   while(t--)   {       scanf("%d",&n);       for(int i=1;i<=n;i++)       {           scanf("%lf%lf%lld",&c[i].x,&c[i].y,&c[i].val);           if(c[i].x==0)            c[i].k=PI/2;           else            c[i].k=atan(c[i].y/c[i].x);       }       sort(c+1,c+1+n);       LL lsum=0,rsum=0;       for(int i=1;i<=n;i++)       {           if(c[i].x>=0)            lsum+=c[i].val;           else            rsum+=c[i].val;       }       LL ans=lsum*rsum;       for(int i=1;i<=n;i++)       {           if(c[i].x>=0)            lsum-=c[i].val,rsum+=c[i].val;           else            rsum-=c[i].val,lsum+=c[i].val;            ans=max(ans,lsum*rsum);       }       printf("%lld\n",ans);   }   return 0;}

HDU 6129 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6129

官方题解:有一个长度为nn的整数序列{a_n}{an},对其做mm次前缀异或和,求最终的序列。

可以发现做m次后,位置为x的初始值对位置为y的最终值的贡献次数是一个只和m与y-x相关的组合式,而我们只关注这个次数的奇偶性。考虑Lucas定理,C(a,b)是奇数当且仅当把a,b二进制表达后b中1的位置是a中1的位置的子集,于是这里所有满足条件的b有很好的性质,对每一个二进制位独立处理就能算出答案。


分析:打表找规律(看b[i]是由前面多少个数异或而来,两个相同数异或为0,我们只需要记录奇偶,看最终结果跟谁有关即可)

          1                2                3                    4                     5
1        (1)           (1,1)         (1,1,1)          (1,1,1,1)           (1,1,1,1,1)
2        (1)           (2,1)         (3,2,1)          (4,3,2,1)           (5,4,3,2,1)
3        (1)           (3,1)         (6,3,1)          (10,6,3,1)         (15,10,6,3,1)
4        (1)           (4,1)         (10,4,1)         (20,10,4,1)       (35,20,10,4,1)
5        (1)           (5,1)         (15,5,1)         (35,15,5,1)       (70,35,15,5,1)
dp[i][j]=dp[i-1][j]+dp[i][j-1];
我们可以发现一个神奇的规律:
m次操作之后,b[0]=a[0];
             b[1]=((C(m,1)%2)*a[0])^(a[1]);
             b[2]=((C(m+1,2)%2)*a[0])^((C(m,1)%2)*a[1])^(a[2]);
             b[3]=((C(m+2,3)%2)*a[0])^((C(m+1,2)%2)*a[1])^((C(m,1)%2)*a[2])^(a[3]);
             b[4]=((C(m+3,4)%2)*a[0])^((C(m+2,3)%2)*a[1])^((C(m+1,2)%2)*a[2])^((C(m,1)%2)*a[3])^(a[4]);
对于样例:3    3
1         2         3
0         1         1
                   a[1]
        a[1]     a[2]
a[1]  a[2]     a[3]
则:b[1]=a[1]
       b[2]=a[1]^a[2];
       b[3]=a[2]^a[3];

CODE:

#include<stdio.h>#include<string.h>#include<algorithm>#include<queue>#include<stack>#include<math.h>#include<map>#include<iostream>#define INF 0x3f3f3f3ftypedef  long long LL;using namespace std;const int maxn=2*1e5+10;int a[maxn];LL exp_mod(LL a,LL b,LL p){    LL res=1;    while(b!=0)    {        if(b&1)            res=(res*a)%p;        a=(a*a)%p;        b>>=1;    }    return res;}LL Comb(LL a,LL b,LL p){    if(b==0||b==a)        return 1;    if(a<b)        return 0;    if(a==b)        return 1;    if(b>a-b)        b=a-b;    LL ans=1,ca=1,cb=1;    for(LL i=0; i<b; ++i)    {        ca=(ca*(a-i))%p;        cb=(cb*(b-i))%p;    }    ans=(ca*exp_mod(cb,p-2,p))%p;}LL Lucas(LL n,LL m,LL p){    LL ans=1;    while(n&&m&&ans)    {        ans=(ans*Comb(n%p,m%p,p))%p;        n/=p;        m/=p;    }    return ans;}LL b[maxn];LL f[25];void init(){    f[0]=1;    for(int i=1; i<20; i++)        f[i]=f[i-1]*2;}int gg[maxn];LL sum[maxn];int main(){    int t,n;    init();    LL m;    scanf("%d",&t);    while(t--)    {        memset(sum,0,sizeof(sum));        memset(gg,0,sizeof(gg));        scanf("%d%lld",&n,&m);//        int pos=lower_bound(f,f+20,n)-f;//        m%=f[pos];//        cout<<f[pos]<<"----"<<f[pos+1]<<endl;        for(int i=0; i<n; i++)            scanf("%d",&a[i]);        LL ans=0;        for(int i=0; i<n; i++)        {            LL kk=Lucas(m+i-1,i,2);//            cout<<kk<<"* ";            if(kk)//看当前这位对结果的影响,如果为0,没任何影响            {//如果为1,则后面所有的数都要依次对0~n-1-i异或                for(int j=i; j<n; j++)                {                    sum[j]^=a[j-i];                }                //cout<<sum[i]<<" ";            }        }        for(int i=0;i<n;i++)        {            if(i)                printf(" ");            printf("%lld",sum[i]);        }        printf("\n");    }    return 0;}
还有一个神奇的做法,看上去很简单,不过不好想,目前还不太明白。

const int N=1e5+10;int lowbit(int x){    return x&(-x);}int a[N*2];int main(){    int t,n,m;    scanf("%d",&t);    while(t--)    {        scanf("%d%d",&n,&m);        a[0]=0;        for(int i=1; i<=n; i++)            scanf("%d",&a[i]);        while(m)        {            int x=lowbit(m);            for(int i=1; i<=n; i++)                if(i-x>=0)                    a[i]^=a[i-x];            m-=x;        }        for(int i=1; i<=n; i++)            printf("%d%c",a[i],i==n?'\n':' ');    }    return 0;}



n