• 转载zroi 9.16普转提(代码是自己的)


    Link:

    A:

    套路题结果想了好久……

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

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

     1 #include<bits/stdc++.h>
     2 #define maxn 100005
     3 using namespace std;
     4 typedef long long ll;
     5 int n,fa[maxn],ch[maxn][2],x;
     6 ll st[maxn],a[maxn];
     7 int top=0;
     8 void dfs(int i){
     9     if(ch[i][0]) dfs(ch[i][0]);
    10     st[++top]=a[i];
    11     if(ch[i][1]) dfs(ch[i][1]);
    12 } 
    13 ll q[maxn];
    14 int rear=0;
    15 void run(ll x){
    16     /*if(x>=q[rear]){
    17         q[++rear]=x;
    18         return;
    19     }
    20     int l=1,r=rear,mid;
    21     while(l<=r){
    22         mid=(l+r)>>1;
    23         if(q[mid-1]<=x&&x<q[mid]){q[mid]=x;return;}
    24         if(x<q[mid-1]) r=mid-1;
    25         else l=mid+1; 
    26     }*/
    27     int t=upper_bound(q+1,q+rear+1,x)-q;
    28     if(t==rear+1) q[++rear]=x;
    29     else q[t]=x;//将刚好大于它的代替 
    30 }
    31 void init(){
    32     scanf("%d",&n);
    33     for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    34     for(int i=2;i<=n;i++){
    35         scanf("%d%d",&fa[i],&x);
    36         ch[fa[i]][x]=i;
    37     }
    38     dfs(1);
    39     
    40     for(ll i=1;i<=top;i++) st[i]=st[i]-i;
    41     
    42     q[0]=-10000000000;
    43     for(int i=1;i<=top;i++) run(st[i]);
    44     
    45     int ans=n-rear;
    46     printf("%d",ans);
    47 }
    48 int main(){
    49     init();
    50     
    51     return 0;
    52 }

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

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

    (其实也可以理解为在严格单增问题中能拓展的条件为a[i]a[k]ika[i]−a[k]≥i−k,那么也就是a[i]ia[k]ka[i]−i≥a[k]−k)

    B:

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

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

    区间最小和gcdgcd明显可以用RMQRMQ维护,但此题卡log2log2,因此只能用STST表来维护

     1 #include<bits/stdc++.h>
     2 #define maxn 500005
     3 using namespace std;
     4 int n,a[maxn],np=0,rt=0;
     5 int gcd(int x,int y){
     6     if(y==0) return x;
     7     else return gcd(y,x%y);
     8 }
     9 int minv[maxn][20],gcdd[maxn][20],logg[maxn],w[25];
    10 void ready(){
    11     w[0]=1;
    12     logg[0]=-1;
    13     for(int i=1;i<=20;i++) w[i]=w[i-1]*2;
    14     for(int i=1;i<=maxn-5;i++) logg[i]=logg[i/2]+1;
    15     for(int i=1;i<=n;i++){
    16         minv[i][0]=gcdd[i][0]=a[i];
    17     }
    18     for(int i=1;i<=logg[n];i++)//加个1保个险 
    19     for(int j=1;j<=n;j++)
    20     if(j+w[i]-1<=n){
    21         minv[j][i]=min(minv[j][i-1],minv[j+w[i-1]][i-1]);//本来是j+(1<<(i-1))-1+1
    22         gcdd[j][i]=gcd(gcdd[j][i-1],gcdd[j+w[i-1]][i-1]);
    23     } 
    24 }
    25 int cd1(int i,int j){
    26     int len=j-i+1;
    27     int t=logg[len];
    28     return min(minv[i][t],minv[j-w[t]+1][t]);
    29 }
    30 int cd2(int i,int j){
    31     int len=j-i+1;
    32     int t=logg[len];
    33     return gcd(gcdd[i][t],gcdd[j-w[t]+1][t]);
    34 }
    35 int st[maxn],top=0,yb[maxn],topp=0;
    36 bool check(int mid){
    37     topp=0;
    38     bool ok=false;
    39     for(int i=1;i<=n-mid+1;i++){
    40         int j=i+mid-1;
    41         if(cd1(i,j)==cd2(i,j)){
    42             ok=true;
    43             yb[++topp]=i;
    44         }
    45     }
    46     if(ok){
    47         top=0;
    48         for(int i=1;i<=topp;i++) st[++top]=yb[i];
    49         return true;
    50     }
    51     return false;
    52 }
    53 void run(){
    54     int A=1,B=n,mid,ans;
    55     while(A<=B){
    56         mid=A+B>>1;
    57         if(check(mid))
    58         ans=mid,A=mid+1;
    59         else B=mid-1;
    60     }
    61     printf("%d %d
    ",top,ans-1);//L-R
    62     for(int i=1;i<=top;i++) printf("%d ",st[i]);
    63 } 
    64 void init(){
    65     scanf("%d",&n);
    66     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    67     ready(); 
    68     run(); 
    69 }
    70 int main(){
    71     init();
    72     
    73     return 0;
    74 }

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

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

    C:

    很像以前冒泡排序相关题的一个TrickTrick:

    由于交换序列是一个排列,因此每次交换后左右不可能再有交换,这样就拆为独立的子问题了

    由于初始状态的值有规律是单调的,因此反向考虑问题:

    对于当前区间[l,r][l,r]枚举交换位置ii,如果交换i,i+1i,i+1后左序列是[l,l+i1][l,l+i−1]的一个排列则计算其贡献

    dp[l][r]=dp[l][l+i1]dp[l+i][r]Ci1rl+12dp[l][r]=dp[l][l+i−1]∗dp[l+i][r]∗Cr−l+1−2i−1

     1 #include<bits/stdc++.h>
     2 #define maxn 55
     3 using namespace std;
     4 typedef long long ll;
     5 const ll mod=1e9+7;
     6 ll c[maxn][maxn],dp[maxn][maxn];
     7 int p[maxn],n;
     8 void ready(){
     9     for(int i=0;i<=50;i++){
    10         c[i][0]=1;
    11         for(int j=1;j<=i;j++)
    12         c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    13     }
    14 }
    15 ll dfs(int len,int now){
    16     if(dp[len][now]!=-1) return dp[len][now];
    17     if(len==1) return dp[len][now]=1;
    18     ll ans=0;int j,k,pos=0,t[maxn];//pos和t只有内定了给,不然要重 函数递归时会改变 
    19     for(int i=1;i<=n;i++)
    20     if(p[i]>=now&&p[i]<now+len) t[++pos]=p[i];//pos==len
    21     for(int i=1;i<pos;i++){
    22         swap(t[i],t[i+1]);//枚举交换的位置
    23         for(j=1;j<=i;j++)//判断是否满足
    24         if(t[j]>=now+i) break;//最多为now+i-1;
    25         for(k=i+1;k<=pos;k++)
    26         if(t[k]<now+i)  break;//最小为now+i; 
    27         if(j>i&&k>pos){
    28             ll t1=dfs(i,now);
    29             ll t2=dfs(pos-i,now+i);
    30             ans=(ans+t1*t2%mod*c[pos-2][i-1]%mod)%mod;//第一个:now~now+i-1 第二个now+i~now+len-1; 
    31                                     //在这之前进行n-2次交换,前i-1次选择前面来处理前面的i个 
    32         }
    33         swap(t[i],t[i+1]);
    34     }
    35     return dp[len][now]=ans; 
    36 }
    37 void init(){
    38     scanf("%d",&n);
    39     for(int i=1;i<=n;i++) scanf("%d",&p[i]);
    40     memset(dp,-1,sizeof(dp));
    41     dfs(n,0);
    42     if(dp[n][0]==-1) printf("%d",0);
    43     else printf("%lld",dp[n][0]);
    44 }
    45 int main(){
    46     ready();
    47     init();
    48     
    49     return 0;
    50 }

    注意这里每次处理的[l,r][l,r]的排列是pp的子序列!

    (处理到该子问题时能保证数在pp中的相对位置不变)

    D:

    关键在于贡献为2R+C2R+C,可以理解为对每一个子集算一次贡献

    接下来算每个集合被包含的期望次数即可:

    res=CinCjnCknummnumCkmres=∑Cni∗Cnj∗Cm−numk−numCmk

    其中numnum为如果ii行jj列全涂黑的个数,预处理组合数即可

     1 #include<bits/stdc++.h>
     2 #define maxn 305
     3 using namespace std;
     4 int n,m,k;
     5 double a[maxn],b[maxn*maxn];//b:(k-x,m-x)/(k,m) 而x最多为n^2
     6 void ready(){
     7     a[0]=1.0;
     8     for(int i=1;i<=n;i++) a[i]=a[i-1]*(n-i+1)*1.0/i;
     9     b[0]=1;
    10     for(int i=1;i<=n*n;i++) b[i]=b[i-1]*(k-i+1)*1.0/(m-i+1); 
    11 } 
    12 void outt(){
    13     printf("1");
    14     for(int i=1;i<=99;i++) printf("0");
    15 }
    16 int main(){
    17     scanf("%d%d%d",&n,&m,&k);
    18     ready();
    19     double ans=0;
    20     for(int i=0;i<=n;i++)
    21     for(int j=0;j<=n;j++){
    22         int x=(i+j)*n-i*j;
    23         if(x>k) break;//再加只会更多 
    24         ans+=a[i]*a[j]*b[x];
    25     } 
    26     if(ans>1e99) outt();
    27     else printf("%lf",ans);
    28     
    29     return 0;
    30 } 
  • 相关阅读:
    luogu P3959 宝藏
    hdu4035 Maze
    [hdu2899]Strange fuction
    luogu4407 [JSOI2009]电子字典 字符串hash + hash表
    SPOJ6717 Two Paths 树形dp
    luogu4595 [COCI2011-2012#5] POPLOCAVANJE 后缀自动机
    后缀数组
    luoguP1659 [国际集训队]拉拉队排练 manacher算法
    luoguP4555 [国家集训队]最长双回文串 manacher算法
    CF17E Palisection 差分+manacher算法
  • 原文地址:https://www.cnblogs.com/degage/p/9683558.html
Copyright © 2020-2023  润新知