[ZROI 9.16模拟赛] Tutorial

Link:

传送门

A:

套路题结果想了好久……

排序二叉树的性质就是中序遍历单调递增

于是只考虑当前树的中序遍历的序列即可,与树的形态无关

将序列改成严格单调增想到最大化不变的数,但直接LIS求的是改为非严格单调增的数

一个将严格单调增问题改为非严格的套路是将数$a_i$替换成$a_i-i$,对转换后序列求LIS即可

(其实也可以理解为在严格单增问题中能拓展的条件为$a[i]-a[k]\ge i-k$,那么也就是$a[i]-i\ge a[k]-k$)

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
#define pb push_back
typedef double db;
typedef long long ll;
typedef pair<int,int> P;
const int MAXN=2e5+10;
ll dat[MAXN],ind[MAXN],tot;
int n,x,y,ch[MAXN][2],dp[MAXN],res;

void dfs(int x)
{
    if(~ch[x][0]) dfs(ch[x][0]);
    ind[++tot]=dat[x];
    if(~ch[x][1]) dfs(ch[x][1]);
}

int main()
{
    scanf("%d",&n);
    memset(ch,-1,sizeof(ch));
    for(int i=1;i<=n;i++) 
        scanf("%lld",&dat[i]);
    for(int i=1;i<n;i++)
        scanf("%d%d",&x,&y),ch[x][y]=i+1;
    dfs(1);dp[0]=-1<<30;
    
    for(int i=1;i<=n;i++) ind[i]-=i;
    for(int i=1;i<=n;i++)
    {
        if(ind[i]>=dp[res]) dp[++res]=ind[i];
        else dp[upper_bound(dp+1,dp+res+1,ind[i])-dp]=ind[i];
    }
    printf("%d",n-res);
    return 0;
}
Problem A

B:

长度可行性单调,对长度二分答案

发现区间$[l,r]$中存在$k$的条件为:$gcd(l...r)=min(l...r)=k$

区间最小和$gcd$明显可以用$RMQ$维护,但此题卡$log^2$,因此只能用$ST$表来维护

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
#define pb push_back
typedef double db;
typedef long long ll;
typedef pair<int,int> P;
const int MAXN=5e5+10;
int res[MAXN],tot;
int n,dat[MAXN],lg2[MAXN],mn[MAXN][50],gcd[MAXN][50];
int GCD(int x,int y){return x%y==0?y:GCD(y,x%y);}

void pre()
{
    lg2[1]=0;
    for(int i=2;i<=n;i++)
        lg2[i]=lg2[i-1]+((1<<(lg2[i-1]+1))==i);
    
    for(int i=n;i>=1;i--)
    {
        mn[i][0]=dat[i];
        for(int j=1;(i+(1<<j)-1)<=n;j++)
            mn[i][j]=min(mn[i][j-1],mn[i+(1<<j-1)][j-1]);
    }
    for(int i=n;i>=1;i--)
    {
        gcd[i][0]=dat[i];
        for(int j=1;(i+(1<<j)-1)<=n;j++)
            gcd[i][j]=GCD(gcd[i][j-1],gcd[i+(1<<j-1)][j-1]);
    }
}
int Query_min(int l,int r)
{
    int t=lg2[r-l+1];
    return min(mn[l][t],mn[r-(1<<t)+1][t]);
}
int Query_gcd(int l,int r)
{
    int t=lg2[r-l+1];
    return GCD(gcd[l][t],gcd[r-(1<<t)+1][t]);
}

bool check(int len)
{
    tot=0;
    for(int i=1;i<=n-len+1;i++)
        if(Query_min(i,i+len-1)==Query_gcd(i,i+len-1))
            res[++tot]=i;
    return tot>0;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) 
        scanf("%d",&dat[i]);
    pre();
    
    int l=1,r=n;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid)) l=mid+1;
        else r=mid-1;
    }
    check(r);
    printf("%d %d\n",tot,r-1);
    for(int i=1;i<=tot;i++) printf("%d ",res[i]);
    return 0;
}
Problem B

如果只有询问用$ST$表$O(1)$询问

同时注意由于对一个数多次求$gcd$不会影响区间$gcd$值,因此可以直接用$ST$表维护$gcd$

C:

D:

关键在与贡献为$2^{R+C}$,可以理解为对每一个子集算一次贡献

接下来算每个集合能产生的期望贡献即可:

$res=\sum C^i_n*C^j_n*\frac{C^{k-num}_{m-num}}{C^k_m}$

其中$num$为如果$i$行$j$列全涂黑的个数,预处理组合数即可

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
#define pb push_back
typedef double db;
typedef long long ll;
typedef pair<int,int> P;
const int MAXN=1e5+10;
int n,m,k;
db res,cn[MAXN],cm[MAXN];

double cal(int i,int j)
{
    int num=n*(i+j)-i*j;
    return k-num<0?0:cm[num];
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    cn[0]=cm[0]=1;
    for(int i=1;i<=n;i++) cn[i]=cn[i-1]*(n-i+1)/i;
    for(int i=1;i<=m;i++) cm[i]=cm[i-1]*(k-i+1)/(m-i+1);
    
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++)
            res+=cn[i]*cn[j]*cal(i,j);
    printf("%.6lf",(res>1e99)?1e99:res);
    return 0;
}
Problem D

猜你喜欢

转载自www.cnblogs.com/newera/p/9669268.html