2017多校7-三道水题

1005:Euler theorem

题意:给一个数a,求(a%x)的值有多少种。

分析:对于大于a/2的数,设为t,那么a%t =(a- t).对于小于等于a/2的数t,必然存在一个整数z,使得 z*t<a,(z+1)*t>a,z*t>a,此时答案就是 a-z*t.(前面已出现的答案)。

对于大于a的数,a%t = a。所以答案就呼之欲出了。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <vector>
#define siz 100005
#define LL long long

using namespace std;
int n,m;
 int euler(int n){
     int res=n,a=n;
     for(int i=2;i*i<=a;i++){
         if(n%i==0){
             res=res/i*(i-1);
             while(n%i==0) n/=i;
         }
     }
     if(n>1) res=res/n*(n-1);
     return res;
}
int main()
{
        int t,a;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&a);
        printf("%d\n",(a+1)/2+1);
    }
    return 0;
}

1011:Kolakoski

题意:给一组数,让你求第n的数。

规律:

Kolakoski序列是一个仅由1和2组成的无限数列,是一种通过“自描述”来定义的数列 。他的前几项为
1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1,2,2,1,2,1,1,2,1,2,2,1,1,…( OEIS上的A000002)
它的定义很简单,若把数列中相同的数定为一组,令a(1)=1,a(2)=2,则a(n)等于第n组数的长度。
可以根据这个定义来推算第三项以后的数:例如由于a(2)=2,因此第2组数的长度是2,因此a(3)=2,;
由于a(3)=2,所以第三组数的长度是2,因此a(4)=a(5)=1;由于a(4)=1,a(5)=1,所以第四组数和第五组数的长度都为1,因此a(6)=2,a(7)=1,以此类推

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <vector>
#define siz 10000100
#define LL long long

using namespace std;
int gp[siz];
int x[siz];
void solve(){
    gp[1] = 1;
    gp[2] = 2;
    //gp[3] = 2;
    x[1] = 1;
    int i = 2;
    int j = 2;
    int flag = 2;
    while(j<siz-3){
        if(gp[i] == 2){
            if(flag == 2){
                x[j] = 2;
                ++j;
                x[j] = 2;
                ++j;
                --flag;
            }
            else{
                x[j] = 1;
                ++j;
                x[j] = 1;
                ++j;
                ++flag;
            }
           // gp[++i] = 2;
        }
        else{
            if(flag == 2){
                x[j] = 2;
                ++j;
                --flag;
            }
            else{
                x[j] = 1;
                ++j;
                ++flag;
            }
            //gp[++i] = 1;

        }
        gp[i+1] = x[i+1];
        ++i;
    }

}
int main()
{
    solve();
    int t,a;
    scanf("%d",&t);

    while(t--)
    {
        scanf("%d",&a);
        printf("%d\n",x[a]);
    }
    return 0;
}
1008:题意:给n个坐标点,以及这个点的价值v,定义两个点之间的价值是vi*vj。现让你画一条过原点的的直线,将平面上的点分成两部分,然后将两部分之间 的两两之间求价值,使得价值最大。

分析:对于平面上所有的点,必然会存在一条直线将他们分成两部分,所以我们可以枚举分成的这些两部分。维护一个最大值。

然后,那条线必然是穿过1,3象限或者2,4象限。我们可以将每个点与原点连线,求得一个斜率,然后按照斜率从小到大排序,开始的那条直线可以先平行于x轴开始,再平行于y轴开始,旋转,每次旋转一个当前两个象限斜率小的那个点,而对于答案,对于条直线分成的两部分,设价值和分别为sum1和sum2,那么答案自然是sum1*sum2.而当我们旋转过一个点时,设转 过斜率的价值和为v(在那个斜率的直线上可能有很多点),那么价值为 (sum2 - v) *(sum1+v).暴力分两次更新就行。

#include <iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define siz 105
const int maxn = 50000;
const double infk = 2000000000;
typedef long long LL;
using namespace std;
struct node
{
    int x,y,w;
    double k;
} G[5][maxn+5];
/*
5
8
2 2 2
1 1 2
3 0 100
1 -2 10
-1 1 5
-1 0 5
-1 -1 10
-1 3 10
*/
int n,tot[5];
LL ans;
int isg(int x,int y)
{
    if(x>=0&&y>=0) return 1;
    if(x<0&&y>=0) return 2;
    if(x<0&&y<0) return 3;
    if(x>=0&&y<0) return 4;
    return 0;
}
double fk(int x,int y)
{
    if(x) return y*1.0/x*1.0;
    return infk*1.0;
}
bool cmp(node A,node B)
{
    return A.k < B.k;
}
void finmin(int &s1,int &s2,LL&cot1,LL&cot2,int g1,int g2)
{
    int i;
    G[g1][tot[g1]+1].k = infk+105;
    G[g2][tot[g2]+1].k = infk+105;
    if(G[g1][s1+1].k*1.0<G[g2][s2+1].k*1.0)
    {
        for(i=s1+1;G[g1][i].k==G[g1][s1+1].k; i++) cot1+=G[g1][i].w;
        s1=i-1;
    }
    else
    {
        if(G[g1][s1+1].k>G[g2][s2+1].k)
        {
            for(i=s2+1;G[g2][i].k==G[g2][s2+1].k; i++) cot2+=G[g2][i].w;
            s2=i-1;
        }
        else
        {
            for(i=s1+1; G[g1][i].k==G[g1][s1+1].k; i++) cot1+=G[g1][i].w;
            s1=i-1;
            for(i=s2+1; G[g2][i].k==G[g2][s2+1].k; i++) cot2+=G[g2][i].w;
            s2=i-1;
        }
    }
}
void ok(int g1,int g2,LL sum1,LL sum2)
{
    LL cot1,cot2;
    int s1=0,s2=0;
    while(s1<tot[g1]&&s2<tot[g2])
    {
        cot1=0,cot2=0;
        finmin(s1,s2,cot1,cot2,g1,g2);
        sum1+=(cot2-cot1), sum2+=(cot1-cot2);
        ans = max(ans,sum1*sum2);
    }
    if(s1<tot[g1])
    {
        cot1=0;
        for(int i=s1+1; i<=tot[g1]; i++)
        {
            cot1+=G[g1][i].w;
            sum1-=cot1, sum2+=cot1;
            ans = max(ans,sum1*sum2);
        }
    }
    else
    {
        cot2=0;
        for(int i=s2+1; i<=tot[g2]; i++)
        {
            cot1+=G[g2][i].w;
            sum1+=cot2, sum2-=cot2;
            ans = max(ans,sum1*sum2);
        }
    }
}
void solve()
{
    LL sum1,sum2;
    sum1 = sum2 = 0;
    for(int i=1; i<=tot[1]; i++) sum1+=G[1][i].w*1ll;
    for(int i=1; i<=tot[2]; i++) sum1+=G[2][i].w*1ll;
    for(int i=1; i<=tot[3]; i++) sum2+=G[3][i].w*1ll;
    for(int i=1; i<=tot[4]; i++) sum2+=G[4][i].w*1ll;
    ans = sum1*sum2;
    for(int i=1; i<=4; i++) sort(G[i]+1,G[i]+tot[i]+1,cmp);
    ok(1,3,sum1,sum2);
    sum1 = sum2 = 0;
    for(int i=1; i<=tot[1]; i++) sum2+=G[1][i].w*1ll;
    for(int i=1; i<=tot[2]; i++) sum1+=G[2][i].w*1ll;
    for(int i=1; i<=tot[3]; i++) sum1+=G[3][i].w*1ll;
    for(int i=1; i<=tot[4]; i++) sum2+=G[4][i].w*1ll;
    ans = max(ans,sum1*sum2);
    ok(2,4,sum1,sum2);
}
int main()
{
    int T,x,y,w,t;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=0; i<5; i++) tot[i] = 0;
        for(int i=1; i<=n; i++)
        {
            scanf("%d %d %d",&x,&y,&w);
            t = isg(x,y);
            G[t][++tot[t]] = (node)
            {
                x,y,w,fk(x,y)
            };
        }
        solve();
        printf("%I64d\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值