SRM 597DIV1

250:

  首先先特判答案不存在的情况.

  再设答案为k,则B[k+1,n]是A的一个子序列,所以,

  做法1,枚举k检查子序列是否成立;

  做法2,反过来想,从后往前看,最长的一个子序列对应了最小答案.

600:

  问题可转化为,找一个凸多边形中是否存在三角形,且三角形与多边形不共顶点.

  1暴力:

  找出所有凸包中的点再找一个三角形,

  找凸包中的所有点:枚举横坐标,再枚举每条边找到上端点和下端点. (O(size*N))

  找三角形: 任取2个点,枚举下一个点,过程中必须保证每个点都不是凸包顶点.(O(size*N))

  2特判:

  如果凸包上点的个数size>=最大坐标差,可以确定其中必定有3不共线的点.

  

int N;
#define INF 200000
#define EPS 1e-10
int findcross(node a,node b,int x,bool flg)
{
    if (a.x==b.x) return flg?max(a.y,b.y):min(a.y,b.y);
    double k = (double)(a.y-b.y)/(double)(a.x-b.x);
    double c = (double)a.y-k*(double)a.x;
    double yy = k*(double)x+c;
    int res;
    if (flg) res = floor(yy+EPS);
    else    res = ceil(yy-EPS);
    if (x==0||x==-1)
    {
    //    printf("seg:(%d,%d) (%d,%d) cross at (%d ,%.2lf) , flg=%d res=%d\n",a.x,a.y,b.x,b.y,x,yy,flg,res);
    }
    return res;
}
int count(vector<int>X,vector<int>Y,vector<node> &q)
{
    int cnt = 0;
    int l,r;
    l = INF, r = -INF;
    for (int i=0 ; i<N ; i++ ) l=min(l,X[i]),r=max(r,X[i]);
    printf("l=%d r=%d\n",l,r);
    for (int i=l ; i<=r ; i++ )
    {
        int yu,yl;
        yu = -INF, yl = INF;
        for (int j=0 ; j<N ; j++ )
        {
            node a = (node){X[j],Y[j]};
            node b = (node){X[(j+1)%N],Y[(j+1)%N]};
            if (min(a.x,b.x)<=i && i<=max(a.x,b.x))
            {
                yu = max(yu,findcross(a,b,i,1));
                yl = min(yl,findcross(a,b,i,0));
            }
        }
    //    if (yu-yl>=0) printf("x=%d yl=%d yr=%d\n",i,yl,yu);
        cnt += yu-yl+1;
        if (cnt>INF+N) return cnt;
        for (int j=yl ; j<=yu ; j++ ) q.push_back((node){i,j});
    }
    return cnt;
}
double xmult(node p1,node p2 ,node p3)
{
//    printf("area: (%d,%d) , (%d,%d) , (%d,%d) = ",p1.x,p1.y,p2.x,p2.y,p3.x,p3.y);
    node a;
    a.x = p1.x-p2.x;
    a.y = p1.y-p2.y;
    node b;
    b.x = p3.x-p2.x;
    b.y = p3.y-p2.y;
    double res = (double)(a.x*b.y-b.x*a.y)/2.0;
//    printf("%lf\n",res);
    return fabs(res);
}
bool ok(node a,vector<int> X,vector<int> Y)
{
    for (int i=0 ; i<N ; i++ ) if (a.x==X[i] && a.y==Y[i]) return false;
    return true;
}
bool check(vector<int>X,vector<int>Y)
{
    vector<node>q;
    int cnt = count(X,Y,q);
    printf("count=%d\n",cnt);
    if (cnt>INF+N) return true;
//    for (int i=0 ; i<q.size() ; i++ ) if (ok(q[i],X,Y))printf("(%d,%d)\n",q[i].x,q[i].y);
    for (int i=0 ; i<q.size() ; i++ )
    if (ok(q[i],X,Y))
    {
        for (int j=i+1 ; j<q.size(); j++ )
        if (ok(q[j],X,Y))
        {
            for (int k = j+1 ; k<q.size() ; k++ )
            if (ok(q[k],X,Y) && xmult(q[i],q[j],q[k])>EPS) return true;
            return false;
        }
    }
    return false; 
}
string ConvexPolygonGame::winner(vector <int> X, vector <int> Y) 
{
    N = X.size();
    if (check(X,Y)) return "Masha";
    else return "Petya";
}
View Code

  trick:

  计算上端点和下端点需要用取整函数,由于精度误差要加EPS:

  上端点 = floor(y+EPS);

  下端点 = ceil(y-EPS);

900:

  通过分析知道确定第一列和"接下来每列新出现的颜色序列"后,可以确定一种方案,同时颠倒第一列可以得到一种对称方案.

  再推导出"新颜色序列"的性质:

    长度为N的序列可以确定长度为N的方案;

    相邻项不相邻;

  问题转化为对于给定的R,G,B,求组成序列的方案数;

  然后计算一系列组合数就可以统计答案(按最后一位的颜色分别计算).

  一个技巧:

  计算 0~n 的 阶乘的逆元,根据模数是prim的性质,利用费马小定理得到n!的逆元f[n],然后由于

    f[n]*n! = f[n]*n*(n-1)! = 1 % MOD

  所以f[n-1] = f[n]*n,递推即可.

  

#define MOD 1000000007
#define MAXN 1500001
typedef long long llong;
llong a[MAXN],inv[MAXN],p[MAXN];
llong Pow(llong num,llong times)
{
    llong res=1;
    while (times)
    {
        if (times%2ll) res = res*num%MOD;
        num = num*num%MOD;
        times/=2ll;
    }
    return res;
}
llong c(int m,int n)
{
    if (n>m||m<0) return 0;
    return a[m]*inv[m-n]%MOD*inv[n]%MOD;
}
llong getans(int x,int y,int z)
{
    llong res=0;
    for (int g=x-1 ; g<=x ; g++ )
    {
        for (int e=0 ; e<=g ; e++ )
        {
            if ((y-z+g-e)%2) continue;
            int oy = (y-z+g-e)/2;
            int oz = g-e-oy;
            int ry = y-oy-e;
            int rz = z-oz-e;
            if (oy<0||oz<0||ry<0||rz<0||ry!=rz) continue;
            res += c(g,e)*c(g-e,oy)%MOD*c(g+ry-1,ry)%MOD*p[e]%MOD;
            while(res>=MOD) res-=MOD;
        }
    }
//    printf("res=%lld\n",res);
    return res;
}
int LittleElephantAndBoard::getNumber(int M, int R, int G, int B) 
{
    for (int i=0 ; i<MAXN ; i++ ) a[i] = i==0?1:a[i-1]*(llong)i%MOD;
    for (int i=0 ; i<MAXN ; i++ ) p[i] = i==0?1:p[i-1]*2ll%MOD;
    inv[MAXN-1] = Pow(a[MAXN-1],MOD-2);
    for (int i=MAXN-2 ; i>=0 ; i-- ) inv[i] = inv[i+1]*((llong)i+1)%MOD;
    llong res=0;
    res += getans(M-R,M-G,M-B)*2LL%MOD; res %= MOD;
    res += getans(M-G,M-R,M-B)*2LL%MOD; res %= MOD;
    res += getans(M-B,M-G,M-R)*2LL%MOD; res %= MOD;
    return (int)res;
}
View Code

 

转载于:https://www.cnblogs.com/eggeek/p/3461366.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值