转载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\n",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 } 

 

posted @ 2018-09-20 21:52  degage  阅读(191)  评论(0编辑  收藏  举报