个人A题笔记(时刻更新)

文章目录

  • 算法归纳及笔记:
    • 一、快速幂乘算法:
    • 二、x&-x
  • A题合集:
    • 计算系数
    • N^N
    • 单词接龙
    • 数数
    • Promble
    • Tokitsukaze and Discard Items
    • 被3整除的子序列(线性DP)

算法归纳及笔记:

一、快速幂乘算法:

要点说明:
b&1表示判断二进制数最低位是否为1.
b>>1表示删去二进制位.

二、x&-x

用以求一个数转化为二进制数后最低位的1,即lowbit,

A题合集:

计算系数

[题目描述]:
给定一个多项式(ax+by)k,请求出多项式展开后xnym项的系数。
[输入]
每组输入数据共一行,包含5个整数,分别为a,b,k,n,m,每两个整数之间用一个空格隔开。
数据规模:
对于30%的数据,有0≤k≤10;
对于50%的数据,有a=1,b=1;
对于100%的数据,有0≤k≤1,000,0≤n, m≤k,且n+m=k,0≤a, b ≤1,000,000。
[输出]
每组输出共1行,包含一个整数,表示所求的系数,这个系数可能很大,输出对10007取模后的结果。

[Sample Input]
1 1 3 1 2
[Sample Output]
3
解题思路:
二项式定理,求C(n,r)时采用费马小定理:如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)
快速幂求逆元再取模。
解题代码:

#include
#define mod 10007
using namespace std;
typedef long long ll;
ll dpow(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans%mod;
}
ll niyuan(ll a ,ll b)
{
    return dpow(a,b-2);
}
ll C2(ll n,ll m)
{
    if(!m)
        return 1;
    ll x=n-m+1;
    ll sum1=1,sum2=1;
    while(n!=x-1)//求A(n,m)
    {
        sum1=sum1*n%mod;
        n--;
    }
    while(m)//求m!
    {
        sum2=sum2*m%mod;
        m--;
    }
    return (sum1*niyuan(sum2,mod))%mod;//返回(A(n,m)*(n!的逆元))%mod
}


int main()
{
    ll a,b,k,n,m;
    ll ans;
    cin>>a>>b>>k>>n>>m;
    ans=(dpow(a,n)*dpow(b,m)*C2(k,m))%mod;
    cout<<ans;
}


N^N

[题目描述]:
现给你一个正整数N,请问N^N的最左边和最右边的数字是什么?
[输入]
输入包含多组测试数据。每组输入一个正整数N(N<=1000000)。
[输出]
对于每组输入,输出N^N的最左边和最右边的数字。

[Sample Input]
3
5
[Sample Output]
2 7
3 5
解题思路:
求最右边的数:使用快速幂对10取余
求最左边的数:用一个变量b存lg(nn),即b=lg(nn)=n*lg(n); 10b=nn;对于10的指数b,可以写成b=x(整数部分)+y(小数部分)(y可以为0),就有10b=10x * 10y=nn;而nn/10x为所求最左边的数,nn/10x=10y,所以用b减去b的整数部分(int b)即可求到10y便为最左边的数。
解题代码:

#include 
#include
typedef long long ll;
using namespace std;
ll qpow(ll x)
{
    ll y=1;
    ll tem=x;
    while(tem)
    {
        if(tem&1)
            y=(y*x)%10;
        x=(x*x)%10;
        tem>>=1;
    }
    return y%10;
}
int main()
{
    ll n,ans;
    double b;
    while(cin>>n)
    {
        b=n*log10(1.0*n);
        b=b-(int)b;
        ans=(int)pow(10.0,b);
        cout<<ans<<" "<<qpow(n)<<endl;
    }
    return 0;
}

单词接龙

[题目描述]:
单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beastbeast和astonishastonish,如果接成一条龙则变为beastonishbeastonish,另外相邻的两部分不能存在包含关系,例如atat 和 atideatide 间不能相连。
[输入]:
输入的第一行为一个单独的整数nn (n \le 20n≤20)表示单词数,以下nn 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在.
[输出]
只需输出以此字母开头的最长的“龙”的长度

[Sample Input]
5
at
touch
cheat
choose
tact
a
[Sample Output]
23
解题思路:
dfs,关键点在龙的判断,这里我使用的是使用接上来的串的第一个字符,从当前的龙尾的最后一个字符开始倒着比较,若相等,则当前龙的那个串方向变换,在一一比较,用sum记录重叠字符。当然这个可能有点饶,洛谷上的解法是一个个试重复字符的个数。
解题代码:

#include 
using namespace std;
string putin[21];
string dr[43];
int vir[21]= {0};
int pul,res=0;
int n;
int  longth(string a,int fg)
{
    int sum=0;
    for(int i=dr[fg].length()-1,j=0; i>0;)
    {

        if(dr[fg][i]==a[j])
        {
            sum++;
            j++;
            i++;
        }
        else if(sum==0)
            i--;
        else
        {
            sum=0;
            break;
        }
        if(i>dr[fg].length()-1&&sum)
            break;

    }
    if(sum)
        return (a.length()-sum);
    return 0;

}
void dfs(int x)
{
    res=max(res,pul);
    for(int i=0; i<n; i++)
    {
        if(vir[i]<2)
        {
            int y=longth(putin[i],x-1);
            if(y)
            {
                dr[x]=putin[i];
            }
            else
                continue;
            pul+=y;
            vir[i]++;
            dfs(x+1);
            pul-=y;
            vir[i]--;
        }
    }
}

int main()
{
    char head;
    cin>>n;
    for(int i=0; i<n; i++)
        cin>>putin[i];
    cin>>head;
    for(int i=0; i<n; i++)
        if(putin[i][0]==head)
        {
            pul=putin[i].length();
            dr[0]=putin[i];
            vir[i]++;
            dfs(1);
        }
    cout<<res;
    return 0;
}

数数

[题目描述]:
给出N,求
∑ i=1 n​ ∑ j=1 n​ ( i×j)

∏ i=1 n ∏ j=1 n ( i×j)
答案对 998244353取模。
[输入]:
多组数据。
第一行一个数 ,表示测试数据组数。
接下来 行,每行一个数 。
[输出]
对于每一组测试数据,输出一行两个数,即这组测试数据的答案。

[Sample Input]
1
1
[Sample Output]
1 1
解题思路:
快速幂+打表。
解题代码:

#include
using namespace std;
typedef long long ll;
const ll mod=998244353;
const ll maxn=10000005;
ll a[maxn]= {0};
ll mp(ll n)
{
    a[0]=1;
    for(int i=2; i<n; i++)
        a[i-1]=(a[i-2]*i)%mod;
}
ll dpow(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1)
            ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans%mod;
}
int main()
{
    int T;
    mp(maxn);
    scanf("%d",&T);
    while(T--)
    {
        ll n;
        scanf("%lld",&n);
        ll ans1;
        ans1=(n*(n+1)/2)%mod;
        printf("%lld ",(ans1*ans1)%mod);
        printf("%lld%c",dpow(a[n-1],2*n),"\n"[T==0]);
    }
    return 0;
}

Promble

[题目描述]:
Polycarp is choosing three problems for creating a programming test. Totally he has nn problems in his list
.
The complexity of the i-th problem equals ri. All problems are numerated from 1 to n.
.
Help Polycarp to choose such three problems a, b and c, so that the complexity of the first problem strictly less than the complexity of second problem and the complexity of the second problem is strictly less than the complexity of the third problem. So, for chosen problems aa, bb and cc it should be true that ra.
.
If Polycarp can choose three problems in different ways, you can print any of them.
[输入]:
The first line of the input contains one integer n (3≤n≤3000) — the number of problems in Polycarp’s list.
The second line of the input contains nn integers r1,r2,…,rn (1≤ri≤10^9), where ri is the complexity of the i-th problem.
[输出]:
If Polycarp has no ways to choose three problems, you should print three numbers -1. Ih there is a way to choose them, you should print three different integers a,b,c (1≤a,b,c≤n), where a is the number of the first chosen problem, b is the number of the second chosen problem and c is the number of the third chosen problem.
[大致题意]:
输入的第一行n,n个问题,
第二行r1~rn:n个问题的复杂度
问能否从中选出递减(不包含相等)的三个数,
能则随意输出一组,不能则输出三个-1.

[Sample Input]
6
3 1 4 1 5 9

5
1 1000000000 1 1000000000 1

9
10 10 11 10 10 10 10 10 1

[Sample Output]
4 1 3

-1 -1 -1

9 8 3
解题思路:
结构体排序,另外需注意时间,比较水的一道题。
解题代码:

#include
using namespace std;
struct pro
{
    int val;
    int num;
} a[3005];
int cmp(pro a,pro b)
{
    return a.val<b.val;
}
int main()
{
    int ans[3]= {-1};
    int n,p=0;
    cin>>n;
    for(int i=1; i<=n; i++)
    {
        cin>>a[i].val;
        a[i].num=i;
    }
    sort(a+1,a+n+1,cmp);
    int tem=-1;
    for(int i=1; i<=n; i++)
    {
        if(a[i].val!=tem)
        {
            ans[p++]=a[i].num;
            tem=a[i].val;
        }
        if(p>2)
            break;
    }
    if(p<3)
        cout<<"-1 -1 -1";
    else
        for(int i=0; i<3; i++)
            cout<<ans[i]<<" ";
    cout<<endl;
    return 0;
}


Tokitsukaze and Discard Items

[题目描述]:
Recently, Tokitsukaze found an interesting game. Tokitsukaze had nn items at the beginning of this game. However, she thought there were too many items, so now she wants to discard mm (1≤m≤n) special items of them.

These nn items are marked with indices from 1 to n. In the beginning, the item with index ii is placed on the ii-th position. Items are divided into several pages orderly, such that each page contains exactly k positions and the last positions on the last page may be left empty.

Tokitsukaze would do the following operation: focus on the first special page that contains at least one special item, and at one time, Tokitsukaze would discard all special items on this page. After an item is discarded or moved, its old position would be empty, and then the item below it, if exists, would move up to this empty position. The movement may bring many items forward and even into previous pages, so Tokitsukaze would keep waiting until all the items stop moving, and then do the operation (i.e. check the special page and discard the special items) repeatedly until there is no item need to be discarded.
个人A题笔记(时刻更新)_第1张图片
Consider the first example from the statement: n=10, m=4, k=5, p=[3,5,7,10].,The are two pages. Initially, the first page is special (since it is the first page containing a special item). So Tokitsukaze discards the special items with indices 3 and 5. After, the first page remains to be special. It contains,[1,2,4,6,7], Tokitsukaze discards the special item with index 7. After, the second page is special (since it is the first page containing a special item). It contains [9,10], Tokitsukaze discards the special item with index 10.
Tokitsukaze wants to know the number of operations she would do in total.

[输入]:
The first line contains three integers n, m and k (1≤n≤10^18, 1≤m≤105, 1≤m,k≤n) — the number of items, the number of special items to be discarded and the number of positions in each page.

The second line contains mm distinct integers p1,p2,…,pm (1≤p1) — the indices of special items which should be discarded.
[输出]:
Print a single integer — the number of operations that Tokitsukaze would do in total.
[大致题意]:
输入的第一个数n为1.2.3.4…n,n个数,
第二个数m为要从中剔除的数的个数,
第三个数k为k个数分成一组,最后不足k个的也要分成一组
一次只能剔除一组中所有要剔除的数,且剔除完后要重新分组。
输出一共要剔除多少次才能将要剔除的数全部剔除完。

[Sample Input]
10 4 5
3 5 7 10

13 4 5
7 8 9 10

[Sample Output]
3
.
1
解题思路:
用ak来进行划分,
用一个num记下前面已剔除的数,当遇到小于a
k的数时num++;
否则答案ans++,然后将积累的num加进d里来模拟1.2.3…n整个数组的移动,清空num并再次判断是否满足条件,满足则num++并continue掉,否则直接使输入的x<=a*k。这里加a时要向上取整。
解题代码:

#include 
using namespace std;
typedef long long ll;
int main()
{
    ll n,m,k;
    ll x;
    cin>>n>>m>>k;
    ll a=1,num=0,ans=0,d=0;
    for(int j=0; j<m; j++)
    {
        cin>>x;
        if(x-d<=a*k)
            num++;
        else
        {
            if(num)
            {
                ans++;
                d+=num;
                num=0;
            }
            if(x-d<=a*k)
            {
                num++;
                continue;
            }
            if(((x-d)-(a*k))%k==0)
                a+=((x-d)-(a*k))/k;
            else
                a+=((x-d)-(a*k))/k+1;
            num++;
        }

    }
    if(num)
        ans++;
    cout<<ans<<endl;
    return 0;
}

被3整除的子序列(线性DP)

[题目描述]:
给你一个长度为50的数字串,问你有多少个子序列构成的数字可以被3整除
答案对1e9+7取模
[输入]:
输入一个字符串,由数字构成,长度小于等于50
[输出]:
输出一个整数

[Sample Input]
132

9

333

00
[Sample Output]
3

1

7

3

来源:牛客oj 21302
解题思路:
首先能被3整除的数,各位数的和加起来一定能整除3。
按线性DP的思想就可以设置一个数组DP[i][j]表示长度为i的序列的所有子序列构成的数除3余j的子序列的个数。
可以得出状态转移方程:
dp[i][j]+=(dp[i-1][j]+dp[i-1][(3+j-m)%3])%mod;
这是因为dp[i][j]由两部分得来
1.长度为i-1的序列的所有子序列构成的数除3余j的子序列的个数。
2.加入第i个数后所构成的子序列中除3余j的子序列的个数。
关键点,(3+j-m)%3怎么来的。
m是第i个数单个数除3的余数,那么如果我们能找到之前的一个子序列各位数和的余数等于3-m,那这个子序列加上这第i个数后构成的子序列就能被3整除了,同理,如果把3变成3+j,那这个子序列加上这第i个数后构成的子序列就能被3+j整除了,即这个子序列构成的数各位和是3+j的倍数,再对这个数%3余数就是j,正好是我们转移方程需要的.
解题代码:

#include
using namespace std;
const int mod=1e9+7;
int main()
{
    string s;
    int dp[100][3];
    cin>>s;
    fill(dp[0],dp[0]+100*3,0);
    dp[0][(s[0]-'0')%3]++;
    for(int i=1; i<s.length(); i++)
    {
        int m=(s[i]-'0')%3;
        for(int j=0; j<3; j++)
        {
            dp[i][j]+=(dp[i-1][j]+dp[i-1][(3+j-m)%3])%mod;
            dp[i][m]%=mod;
        }
        dp[i][m]++;
    }
    cout<<(dp[s.length()-1][0])%mod;
    return 0;
}

你可能感兴趣的:(个人A题笔记(时刻更新))