11月7日——离noip还有12天

来源:互联网 发布:c语言移位运算 编辑:程序博客网 时间:2024/05/17 00:14

高中第一次这么认真地整理题~~~

目录

  • 目录
    • A题
      • 问题描述
      • 代码dfs就可以了
    • B题
      • 问题描述
      • 题解
      • 标答一
      • 标答二
    • C题
      • 问题描述
      • 题解
      • 正解
    • summary
    • 博主郑重承诺以后的博客上一题一图片

本次考试没有用黑科技(random),就GG了;
感觉内心很惶恐,吃枣药丸。

A题

这里写图片描述

问题描述:

小A得到了一棵美丽的有根树。这棵树由n个节点以及n - 1条有向边构成,每条边都从父亲节点指向儿子节点,保证除了根节点以外的每个节点都有一个唯一的父亲。树上的节点从1到n标号。该树的一棵子树的定义为某个节点以及从该节点出发能够达到的所有节点的集合,显然这棵树共有n棵子树。小A认为一棵有根树是美丽的当且仅当这棵树内节点的标号构成了一个连续的整数区间。现在小A想知道这棵树上共有多少棵美丽的子树。
输入:
第一行有一个整数n,表示树的节点数。
接下来n–1行,每行两个整数u,v,表示存在一条从u到v的有向边。
输入保证该图是一棵有根树。
输出:
输出一个整数占一行,表示对应的答案。

样例输入:
4
2 3
2 1
2 4
样例输出:
3
数据范围:
对于20%的数据,1 ≤ n ≤ 1000。
对于100%的数据,1 ≤ n ≤ 100000。

so easy~~~~~~~~~
简单题,一遍dfs 求出每棵子树中最小顶点编号,最大顶点编号以及子树大小即可判断该子
树是否满足要求。

代码(dfs就可以了)

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#include<queue>#define L(u) (u<<1)#define R(u) (u<<1|1)#define LL long long using namespace std;const int M=100005;int head[M*2],cnt,fa[M*2],root,ans,maxn[M*2],minn[M*2],tot[M*2];struct Edge{    int to,next;}edge[M*2];int A[M*2],m,n,k,p,l,flag,a,b,c;void read(int &n){    char c=getchar();n=0;    while(c<'0'||c>'9') {c=getchar();continue;}    while(c>='0'&&c<='9'){n=n*10+c-'0';c=getchar();} }void add(int from,int to){    edge[++cnt].next=head[from];    edge[cnt].to=to;    head[from]=cnt;}void dfs1(int s,int f){    tot[s]=1;    maxn[s]=s;minn[s]=s;    for (int i=head[s];i!=0;i=edge[i].next){        int v=edge[i].to;        if(v!=f){            dfs1(v,s);            tot[s]+=tot[v];            maxn[s]=max(maxn[s],maxn[v]);            maxn[s]=max(maxn[s],v);            minn[s]=min(minn[s],minn[v]);            minn[s]=min(minn[s],v);        }    }}int main(){    freopen("A.in","r",stdin);    freopen("A.out","w",stdout);    memset(maxn,0,sizeof(maxn));    memset(minn,1000000007,sizeof(minn));    read(n);    for (int i=1;i<=n-1;i++){        read(a);read(b);        add(a,b);fa[b]=a;    }    for (int i=1;i<=n;i++)        if(fa[i]==0){fa[i]=-1;root=i;break;}    dfs1(root,-1);    for (int i=1;i<=n;i++)        if(maxn[i]-minn[i]+1==tot[i])             ans++;    printf("%d",ans);    return 0;}

B题

这里写图片描述

问题描述:

对于一个排列,考虑相邻的两个元素,如果后面一个比前面一个大,表示这个位置是上升的,用I表示,反之这个位置是下降的,用D表示。如排列3,1,2,7,4,6,5可以表示为DIIDID。
现在给出一个长度为n-1的排列表示,问有多少种1到n的排列满足这种表示。
输入:
一个字符串S,S由I,D,?组成。?表示这个位置既可以为I,又可以为D。
输出:
有多少种排列满足上述字符串。输出排列数模1000000007。

样例输入:
?D
样例输出:
3
数据范围:
对于20%的数据,S长度≤ 10;
对于100%的数据,S长度 ≤ 1000。

这道题还有原题。。。(无语)

题解

用dp[i][j]表示考虑了排列的前i个数,其中最后一个数在前i个数中是第j大的,并且符合前i− 1个字符的这样的方案数。
考虑s[i − 1] = ′I′时的转移:
dp[i][j] = ∑(j−1)(k=1)dp[i− 1][k] ,只需要对dp维护一个前缀和即可以做到O(1)地转移。
在要求为下降或者没有限制的时候同理。

标答一

#include <cstdio>#include <cstring>#include <algorithm>#define maxn 1009using namespace std;char s[maxn];int f[maxn][maxn],sum[maxn][maxn];const int MOD=1000000007;int main(){    freopen("b.in", "r", stdin);    freopen("b.out", "w", stdout);      while(scanf("%s",s+1)!=EOF)    {        int n=strlen(s+1);        memset(f,0,sizeof(f));        memset(sum,0,sizeof(sum));        f[1][1]=1;        sum[1][1]=1;        for(int i=2;i<=n+1;i++)        {            int cur=n+2-i;            for(int j=1;j<=i;j++)            {                if(s[cur]=='D')                {                    if(j==1)                        f[i][j]=0;                    else                        f[i][j]=(f[i][j]+sum[i-1][j-1])%MOD;                }                else if(s[cur]=='I')                {                    if(j==i)                        f[i][j]=0;                    else                    {                        f[i][j]=(f[i][j]+sum[i-1][i-1])%MOD;                        f[i][j]=(f[i][j]-sum[i-1][j-1])%MOD;                        f[i][j]+=MOD;                        f[i][j]%=MOD;                    }                }                else                {                    f[i][j]=(f[i][j]+sum[i-1][i-1])%MOD;                }            }            for(int j=1;j<=i;j++)                sum[i][j]=(sum[i][j-1]+f[i][j])%MOD;        }        printf("%d\n",sum[n+1][n+1]);    }    //system("pause");    return 0;}

标答二

巧用位运算加滚动数组(上面那个连接里博主的代码)

#include <cstdio>#define NAME "B"int dp[2][1005];const int mod = 1000000007;int main() {    freopen(NAME".in","r",stdin);    freopen(NAME".out","w",stdout);    dp[0][1] = 1;    int cur = 0;    char x = getchar();    int i = 1;    while(x == 'I'||x == 'D'||x == '?') {        cur ^= 1;i++;        for(int j = 1;j < i;j++)dp[cur^1][j] += dp[cur^1][j-1],dp[cur^1][j] %= mod,dp[cur][j] = 0;        if(x == 'I') {            for(int j = 2;j <= i;j++)dp[cur][j] = dp[cur^1][j-1],dp[cur][j] %= mod;        }        if(x == 'D') {            for(int j = 1;j < i;j++)dp[cur][j] = dp[cur^1][i-1] - dp[cur^1][j-1] + mod,dp[cur][j] %= mod;        }        if(x == '?') {            for(int j = 2;j <= i;j++)dp[cur][j] = dp[cur^1][j-1],dp[cur][j] %= mod;            for(int j = 1;j < i;j++)dp[cur][j] += dp[cur^1][i-1] - dp[cur^1][j-1] + mod,dp[cur][j] %= mod;        }        x = getchar();    }    int ans = 0;    for(int j = 1;j <= i;j++)ans = (ans + dp[cur][j]) % mod;    printf("%d",ans);    return 0;} 

C题

这里写图片描述

问题描述:

小A非常喜欢字符串,所以小K送给了小A两个字符串作为礼物。两个字符串分别为X,Y。小A非常开心,但在开心之余她还想考考小K。小A定义L为X与Y的最长公共子序列的长度(子序列在字符串内不一定连续,一个长度为L的字符串有2^L个子序列,包括空子序列)。现在小A取出了X的所有长度为L的子序列,并要求小K回答在这些子序列中,有多少个是Y的子序列。因为答案可能很大,所以小K只需要回答最终答案模10^9 + 7。
输入:
第一行包含一个非空字符串X。
第二行包含一个非空字符串Y。
字符串由小写英文字母构成。
输出:
对于每组测试数据输出一个整数,表示对应的答案。

样例输入:
aa
ab
样例输出:
2
数据范围:
对于20%的数据,1 ≤ |X|,|Y| ≤ 10;
对于100%的数据,1 ≤ |X|,|Y| ≤ 1000。

题解

首先用O(n2)的动态规划处理出dp数组,dp[i][j]表示X 串的前i 个字符和Y 串的前j 个字符的最长公共子序列的长度,在这个基础上再进行一个动态规划。用f[i][j]表示在X 串的前i个字符中,有多少个长度为dp[i][j]的子序列在Y 的前j 个字符中也出现了。转移:若
dp[i − 1][j] ==dp[i][j],则f[i][j]+= f[i − 1][j],表示i 这个字符不选;再考虑选i 这个字
符,找到Y 串前j 个字符中最靠后的与X[i]相同的字符的位置,设为p,若dp[i− 1][p− 1] + 1 == dp[i][j],则f[i][j]+= f[i − 1][p− 1]。最终的答案即为f[n][m]。
照着题解做就能写出代码AC

正解

#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <algorithm>#include <queue>#include <map>#include <set>#include <vector>#include <string>#include <stack>#include <bitset>#define INF 0x3f3f3f3f#define eps 1e-8#define FI first#define SE secondusing namespace std;typedef long long LL;const int N = 1005;const int Mod = 1e9 + 7;char A[N], B[N];int dp[N][N], f[N][N];int pre[N][26], pos[30];inline void add(int &x, int v) {    x += v;    if(x >= Mod) x -= Mod;}int main() {    freopen("c.in", "r", stdin);    freopen("c.out", "w", stdout);          scanf("%s%s", A + 1, B + 1);        int n = strlen(A + 1);        int m = strlen(B + 1);        for(int i = 1; i <= n; ++i) {            for(int j = 1; j <= m; ++j) {                dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);                if(A[i] == B[j]) dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);            }        }        memset(pos, -1, sizeof(pos));        for(int i = 1; i <= m; ++i) {            pos[B[i] - 'a'] = i;            for(int j = 0; j < 26; ++j) pre[i][j] = pos[j];        }        for(int i = 0; i <= n; ++i) f[i][0] = 1;        for(int i = 0; i <= m; ++i) f[0][i] = 1;        for(int i = 1; i <= n; ++i) {            for(int j = 1; j <= m; ++j) {                f[i][j] = 0;                if(dp[i][j] == dp[i - 1][j]) add(f[i][j], f[i - 1][j]);                int p = pre[j][A[i] - 'a'];                if(p != -1 && dp[i - 1][p - 1] + 1 == dp[i][j]) add(f[i][j], f[i - 1][p - 1]);            }        }        printf("%d\n", f[n][m]);    return 0;}

summary

(博主郑重承诺以后的博客上一题一图片

离noip不到两周了,加油努力!!!fighting!!

1 0
原创粉丝点击