超级码力在线编程大赛初赛 第2场 T1-T4题解

T3.五字回文

class Solution {
    
    
public:
    /**
     * @param s: The given string
     * @return: return the number of Five-character palindrome
     */
    int Fivecharacterpalindrome(string &s) {
    
    
        // write your code here
        int sz=s.size();
        string t;
        int ans=0;
        for(int i=0;i<sz-4;i++)
        {
    
    
            t=s.substr(i,5);
            if(t[0]==t[4]&&t[1]==t[3]&&t[0]!=t[1]&&t[0]!=t[2]&&t[1]!=t1[2])ans++;
        }
        return ans;
    }
};

T2.区间异或

一开口就知道是老数据结构题了,经典的求区间最大值,数据结构用ST表或线段树均可。
(ST表的模板来自于这篇文章

const int N=5e4+10;
int x,y,k,a[N],lg[N],fmx[N][21],fmi[N][21];
vector<int>t;
class Solution {
    
    
public:
    /**
     * @param num: array of num
     * @param ask: Interval pairs
     * @return: return the sum of xor
     */
    int Intervalxor(vector<int> &num, vector<vector<int>> &ask) {
    
    
        // write your code here
        int n=num.size();
        lg[0]=-1;
        for(int i=1;i<=n;i++)
        {
    
    
            a[i]=num[i-1]; // 先把给出的num数组存到a数组中,方便下标从1开始
            fmx[i][0]=a[i];//从i开始的连续2^0个数的最大值就等于a[i]本身
            fmi[i][0]=a[i];
            lg[i]=lg[i/2]+1;//预处理log2(i),因为cmath库中自带的函数log2(x)速度较慢
        }
        for(int j=1;j<=20;j++)//O(nlogn)的预处理
            for(int i=1;i+(1<<j)-1<=n;i++)//i+2^j-1不能超过边界n
            {
    
    
                fmx[i][j]=max(fmx[i][j-1],fmx[i+(1<<(j-1))][j-1]);
                //把[i,i+2^j-1]分成左区间[i,i+2^(j-1)-1]和右区间[i+2^(j-1),i+2^j-1],取较大值
                fmi[i][j]=min(fmi[i][j-1],fmi[i+(1<<(j-1))][j-1]);
            }
        int q=ask.size();
        int ans=0;
        for(int i=0;i<q;i++)
        {
    
    
            x=ask[i][0];
            y=ask[i][1];
            k=lg[y-x+1];//k为方程2^k<=y-x+1的解的最大值,即log2(y-x+1)向下取整
            int mx=max(fmx[x][k],fmx[y-(1<<k)+1][k]);
            x=ask[i][2];
            y=ask[i][3];
            k=lg[y-x+1];
            int mi=min(fmi[x][k],fmi[y-(1<<k)+1][k]);
            ans=ans^(mx+mi);
        }
        return ans;
    }
};

T1.三角魔法

计算几何的经典题,运用叉乘(向量积)来判断一个点P是否在三角形ABC内部。
在这里插入图片描述
(上图中i,j,k为x,y,z轴的单位向量)

简单介绍一下原理:叉乘 a ⃗ × b ⃗ \vec{a}×\vec{b} a ×b 垂直于 a ⃗ \vec{a} a b ⃗ \vec{b} b 组成的平面,根据 a ⃗ × b ⃗ \vec{a}×\vec{b} a ×b 的正负,可得到 a ⃗ \vec{a} a b ⃗ \vec{b} b 的相对位置关系。设三维空间中的平面向量 a ⃗ = ( x 1 , y 1 , 0 ) , b ⃗ = ( x 2 , y 2 , 0 ) \vec{a}=(x_1,y_1,0),\vec{b}=(x_2,y_2,0) a =(x1,y1,0),b =(x2,y2,0),那么 a ⃗ × b ⃗ = ( 0 , 0 , x 1 y 2 − x 2 y 1 ) \vec{a}×\vec{b}=(0,0,x_1y_2-x_2y_1) a ×b =(0,0,x1y2x2y1),可用右手定则判断 a ⃗ × b ⃗ \vec{a}×\vec{b} a ×b 的方向。若 x 1 y 2 − x 2 y 1 > 0 x_1y_2-x_2y_1>0 x1y2x2y1>0,则 b ⃗ \vec{b} b a ⃗ \vec{a} a 的左侧,从 a ⃗ \vec{a} a b ⃗ \vec{b} b 是逆时针,如下图:
在这里插入图片描述
判断一个点P是否在三角形ABC内部,那么求t1=AB×AP,t2=AB×AC,若t1*t2>=0,说明P,C在AB的同一侧(t1*t2>0) 或者 P,C中至少有一个点在AB上(t1*t2=0);其他两种情况类似判断(P,A在BC的同一侧、P,B在CA的同一侧)。

注意还要判断三个点是否共线,共线就不能组成三角形了。

typedef long long ll;
class Solution {
    
    
public:
    /**
     * @param triangle: Coordinates of three points
     * @param point: Xiaoqi's coordinates
     * @return: Judge whether you can cast magic
     */
    bool istr(int x1,int y1,int x2,int y2,int x3,int y3) // 判断三点是否共线
    {
    
    
        ll a=(x3-x1)*(y2-y1);
        ll b=(x2-x1)*(y3-y1);
        return a!=b; // 不共线,三点可组成三角形
    }
    bool judge(int x1,int y1,int x2,int y2,int x3,int y3,int x,int y)
    // A(x1,y1) B(x2,y2) C(x3,y3) P(x,y)
    // AB=(x2-x1,y2-y1)
    // AC=(x3-x1,y3-y1)
    // BC=(x3-x2,y3-y2)
    // AP=(x-x1,y-y1)
    // BP=(x-x2,y-y2)
    // CP=(x-x3,y-y3)
    // 判断(x,y)是否在其他三点的内部
    {
    
    
        ll d=(y-y1)*(x2-x1)-(y2-y1)*(x-x1); // AB×AP
        ll q=(y3-y1)*(x2-x1)-(y2-y1)*(x3-x1); // AB×AC
        if(d*q<0) return false; // 两个叉积异号,说明P,C不在AB的同一侧,那么P在外部
        d=(y-y2)*(x3-x2)-(y3-y2)*(x-x2);  // BC×BP
        q=(y1-y2)*(x3-x2)-(y3-y2)*(x1-x2); // BC×BA
        if(d*q<0) return false; // 两个叉积异号,说明P,A不在BC的同一侧,那么P在外部
        d=(y-y3)*(x1-x3)-(y1-y3)*(x-x3); // CA×CP
        q=(y2-y3)*(x1-x3)-(y1-y3)*(x2-x3); // CA×CB
        if(d*q<0) return false; // 两个叉积异号,说明P,B不在CA的同一侧,那么P在外部
        return true;
    }
    string castMagic(vector<vector<int>> &triangle, vector<int> &point) {
    
    
        // write your code here
        int ans1=istr(triangle[0][0],triangle[0][1],triangle[1][0],triangle[1][1],triangle[2][0],triangle[2][1]);
    	int ans2=judge(triangle[0][0],triangle[0][1],triangle[1][0],triangle[1][1],triangle[2][0],triangle[2][1],point[0],point[1]);
        if(ans1&&ans2)return "Yes";
        else return "No";
    }
};

T4.小栖的金字塔

首先说明一下,这题是个固定的数列,前人已经研究出公式了,我只是在OEIS上查找这个数列…

用f(i)表示从左上角(1,1)到右下角(i,i)的方案数。
先写个打表代码,打表前10项,看看有没有规律。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=10;
ll dp[N+10][N+10];
int main()
{
    
    
    ios::sync_with_stdio(false);
    for(int i=1;i<=N;i++)
    {
    
    
        dp[i][1]=1;
        for(int j=2;j<=i;j++)
        {
    
    
            dp[i][j]=dp[i][j-1]+dp[i-1][j-1];
            if(j!=i)dp[i][j]+=dp[i-1][j];
        }
        printf("i=%d dp[i][i]=%lld\n",i,dp[i][i]);
    }
    return 0;
}

得到前10项:
i=1 dp[i][i]=1
i=2 dp[i][i]=2
i=3 dp[i][i]=6
i=4 dp[i][i]=22
i=5 dp[i][i]=90
i=6 dp[i][i]=394
i=7 dp[i][i]=1806
i=8 dp[i][i]=8558
i=9 dp[i][i]=41586
i=10 dp[i][i]=206098

1, 2, 6, 22, 90, 394, 1806, 8558, 41586, 206098看起来确实没什么规律,去OEIS查一下,找到了题目要求的这个数列:http://oeis.org/A006318,Large Schröder numbers(大施罗德数)。

网页显示,Twice A001003 (except for the first term),也就是说,我们要求的大施罗德数是A001003这个数列中每个数的两倍(除了第一项)。

打开A001003这个数列:http://oeis.org/A001003,super-Catalan numbers or little Schroeder numbers(超级卡特兰数/小施罗德数)。找到求超级卡特兰数的公式:

D-finite with recurrence: (n+1) * a(n) = (6*n-3) * a(n-1) - (n-2) * a(n-2) if n>1. a(0) = a(1) = 1.

按上述公式O(n)递推求出超级卡特兰数,除了第一项(注意这里的第一项下标从0开始),大施罗德数 = 超级卡特兰数 * 2。

typedef long long ll;
const int N=1e7,mod=1e9+7;
ll f[N+10];
ll qpow(ll a,ll b)
{
    
    
    ll s=1;
    while(b)
    {
    
    
        if(b&1)s=s*a%mod;
        a=a*a%mod;
        b/=2;
    }
    return s;
}
ll inv(ll a)
{
    
    
    return qpow(a,mod-2);
}
class Solution {
    
    
public:
    /**
     * @param n: The number of pyramid levels n
     * @param k: Possible coordinates k
     * @return: Find the sum of the number of plans
     */
    int pyramid(int n, vector<int> &k) {
    
    
        // write your code here
        int sz=k.size();
        int ans=0;
        for(int i=0;i<=n;i++)
        {
    
    
            if(i<=1)f[i]=1; // f[0]=f[1]=1;
            else f[i]=((6*i-3)*f[i-1]%mod-(i-2)*f[i-2]%mod+mod)%mod*inv(i+1)%mod;
        }
        for(int i=0;i<sz;i++)
        {
    
    
            int pos=n-k[i];
            if(pos==0)ans=(ans+f[pos])%mod;
            else ans=(ans+f[pos]*2)%mod;
        }
        return ans;
    }
};

猜你喜欢

转载自blog.csdn.net/ljw_study_in_CSDN/article/details/108304794