NOIP模拟题 [LIS][建图][递推][容斥]

来源:互联网 发布:智能数据盒子 编辑:程序博客网 时间:2024/05/20 09:22

正确评估题目难度。

T1:
题意:
给定一棵树,求使得每一个节点的左子树任意值都比当前节点值小,右子树任意值都比当前节点大所需要的最小改动点数。
分析:
首先对于这种神奇的递归定义,我们通过传递大小关系可以发现,通过上推我们可以确定任意两点之间的大小关系,然后很容易想到把树上的点排序。
所以如果先忽略掉“严格递增”和“可能中间不能加数”,这道题显然可以直接贪心减去LIS。
那么这种算法会遇到什么问题呢,显然就是可能有的序列中间是不能加数的!
那么怎么解决呢,很容易可以发现两个之间可以有的数是两数差
于是,当当当,算法出来了,就减一下就可以啦(其实也可以不减,每次比较,但是非常麻烦!预处理大法好!)

#include<cstdio>#include<iostream>#include<cmath>#include<cstdlib>#include<cstring>#include<string>#include<algorithm>#include<queue>#include<set>#include<map>#include<stack>#include<vector>#include<ctime>#define ll long long #define inf 2e18#define clr(x) memset(x,0,sizeof(x))#define maxen(x) memset(x,127,sizeof(x))#define maxer(x) memset(x,31,sizeof(x))#define minus(x) memset(x,-1,sizeof(x))#define each(i,n,m) for(int i=n;i<m;i++)#define eachrev(i,n,m) for(int i=n;i>m;i--)#define minn(a,b,c) min(a,min(b,c))#define maxx(a,b,c) max(a,max(b,c))#ifdef WIN32#define lld "%I64d"#else#define lld "%lld"#endif#define PROC "tree"//for(int i=1;i<=n;i++)//(double) (ll) LL (int)//(double)clock()/CLOCKS_PER_SECusing namespace std;const int Maxn=1e5+5;const int modd=1e9+7;int n,tmp1,tmp2,idx;int a[Maxn],rs[Maxn],ls[Maxn],rk[Maxn];ll f[Maxn];int cnt;int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}void init(){    n=read();    for(int i=1;i<=n;i++)        a[i]=read();        for(int i=2;i<=n;i++){            tmp1=read();tmp2=read();            if(tmp2)rs[tmp1]=i;            else ls[tmp1]=i;    }}void dfs(int u){    if(ls[u])dfs(ls[u]);    rk[++idx]=u;    if(rs[u])dfs(rs[u]);}void work(){    dfs(1);f[cnt+1]=inf;    for(int i=1;i<=n;i++){        a[rk[i]]-=i;        int pos=upper_bound(f+1,f+cnt+2,a[rk[i]])-f;        f[pos]=a[rk[i]];        if(pos>cnt){            cnt++;f[cnt+1]=inf;        }    }    printf("%d",n-cnt);}void debug(){    //}int main(){    freopen(PROC".in","r",stdin);    freopen(PROC".out","w",stdout);    init();    work();    //debug();    return 0;}

T2:
题意:
给定一些数,求最后一个不能被组合出来的数。
分析:
久见此题,第一次真的写。
首先我们可以知道一个简单的判定,即如果一个大于等于最小单个数的区间都可以被组成的话,显然后面就都可以被组成了。
然后我们考虑“缩点”,因为mod最小数的值一样的点之间可以通过加上最小数到达,所以可以放在一个集合,只需要求出每个集合能被组成的最小数,则这个集合剩下的数都可以被组成。
即:根据相同性质减少工作量;
然后算出到每个集合的最短路(即最小能被组成的数)即可。
SPFA大法好,再写错我就去撞墙。

#include<cstdio>#include<iostream>#include<cmath>#include<cstdlib>#include<cstring>#include<string>#include<algorithm>#include<queue>#include<set>#include<map>#include<stack>#include<vector>#include<ctime>#define ll long long #define inf 2e8#define clr(x) memset(x,0,sizeof(x))#define maxen(x) memset(x,127,sizeof(x))#define maxer(x) memset(x,31,sizeof(x))#define minus(x) memset(x,-1,sizeof(x))#define each(i,n,m) for(int i=n;i<m;i++)#define eachrev(i,n,m) for(int i=n;i>m;i--)#define minn(a,b,c) min(a,min(b,c))#define maxx(a,b,c) max(a,max(b,c))#ifdef WIN32#define lld "%I64d"#else#define lld "%lld"#endif#define PROC "bullpen"//for(int i=1;i<=n;i++)//(double) (ll) LL (int)//(double)clock()/CLOCKS_PER_SECusing namespace std;const int Maxn=3e3+5;const int modd=1e9+7;int h[Maxn],usef[Maxn],f[Maxn],mapy[Maxn][Maxn];int n,m,tmp,idx,ans,s,p=2e9;queue<int>que;int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}int gcd(int a,int b){    return b==0?a:gcd(b,a%b);}void init(){    n=read();    m=read();    maxen(usef);    maxer(f);    maxer(mapy);    for(int i=1;i<=n;i++){        tmp=read();        for(int j=0;j<=m;j++)        if(j<tmp){h[tmp-j]=1;p=min(p,tmp-j);}        else break;    }    s=p;    for(int i=1;i<=3000;i++)    if(h[i]){        s=gcd(i,s);        usef[i%p]=min(usef[i%p],i);    }}void work(){    if(s!=1||p==1){        printf("-1");        return;    }    for(int i=0;i<p;i++)        for(int j=1;j<p;j++)            if(i==j)mapy[i][j]=1;            else if(usef[(j-i+p)%p])            mapy[i][j]=usef[(j-i+p)%p];    que.push(0);f[0]=0;    while(!que.empty()){        int cur=que.front();que.pop();        for(int j=1;j<p;j++)            if(f[j]>f[cur]+mapy[cur][j]){                f[j]=f[cur]+mapy[cur][j];                que.push(j);            }    }    for(int i=1;i<p;i++)        if(f[i]<=9e6)        ans=max(ans,f[i]-p);    printf("%d",ans);}void debug(){    //}int main(){    freopen(PROC".in","r",stdin);    freopen(PROC".out","w",stdout);    init();    work();    //debug();    return 0;}

T3:
题意:
求k种元素,每种N个,组成长【k,n】的,每种至少一个的序列的方案数。
分析:
考虑到每种都只要有一种,则可以很容易得到递推式。
然后考虑容斥原理,因为如果我们直接算,则可能会有一些相同的情况被重复计算,然后就减去了不成立的情况的情况(但是不会重复算,即不会对相同序列算两次!这是排列的性质,要想清楚啊!)
然后用容斥减去不成立的情况即可。
做递推不要玄学啊。

#include<cstdio>#include<iostream>#include<cmath>#include<cstdlib>#include<cstring>#include<string>#include<algorithm>#include<queue>#include<set>#include<map>#include<stack>#include<vector>#include<ctime>#define ll long long #define inf 2e8#define clr(x) memset(x,0,sizeof(x))#define maxen(x) memset(x,127,sizeof(x))#define maxer(x) memset(x,31,sizeof(x))#define minus(x) memset(x,-1,sizeof(x))#define each(i,n,m) for(ll i=n;i<m;i++)#define eachrev(i,n,m) for(ll i=n;i>m;i--)#define minn(a,b,c) min(a,min(b,c))#define maxx(a,b,c) max(a,max(b,c))#ifdef WIN32#define lld "%I64d"#else#define lld "%lld"#endif#define PROC "pearl"//for(ll i=1;i<=n;i++)//(double) (ll) LL (ll)//(double)clock()/CLOCKS_PER_SECusing namespace std;const ll Maxn=35;const ll modd=1234567891;ll t,n,k;ll ans;ll read(){    ll x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}void init(){    t=read();}ll qm(ll a,ll b){    ll ret=1;    while(b){        if(b&1)(ret*=a)%=modd;        (a*=a)%=modd;        b>>=1;    }    return ret;}ll jc(ll n){    ll ret=1;    for(ll i=1;i<=(ll)n;i++)        (ret*=i)%=modd;    return ret;}void work(){    for(ll i=1;i<=t;i++){        n=read();        k=read();        ans=0;        ll  flg=1;        for(ll j=0;j<k-1;j++){            ll c=jc(k)*qm(jc(j),modd-2)%modd*qm(jc(k-j),modd-2)%modd;            ll lc=qm(k-j,k)*(qm(k-j,n+1-k)%modd+modd-1LL)%modd*qm(k-j-1,modd-2)%modd;            (ans+=(c*lc%modd)%modd*flg+modd)%=modd;            flg=0-flg;        }            (ans+=(ll)k*(ll)(n-k+1)%modd*flg+modd)%=modd;            printf(lld"\n",ans);    }}void debug(){    //}int main(){    freopen(PROC".in","r",stdin);    freopen(PROC".out","w",stdout);    init();    work();    //debug();    return 0;}
0 0
原创粉丝点击