Loading

Noip模拟55 2021.9.17(打表大胜利)

T1 skip

普通$dp$很好打:

$f[i]=max(f[j]-\sum_{k=1}^{K}k+a_i)$

就是要注意边界问题很烦人。

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 namespace AE86{
 5     inline int read(){
 6         int x=0,f=1;char ch=getchar();
 7         while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 8         while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;
 9     }inline void write(int x,char opt='\n'){
10         char ch[20];int len=0;if(x<0)x=~x+1,putchar('-');
11         do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
12         for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);}
13 }using namespace AE86;
14 
15 const int NN=1e5+5,inf=0x3fffffff;
16 int n,a[NN],ans=-inf;
17 int dp[NN];
18 inline int sigma(int st,int ed){
19     if(st>ed) return 0;
20     return (st+ed)*(ed-st+1)/2;
21 }
22 
23 namespace WSN{
24     inline short main(){
25         freopen("skip.in","r",stdin);
26         freopen("skip.out","w",stdout);
27         n=read();for(int i=1;i<=n;i++) a[i]=read();
28         for(int i=1;i<=n;i++) dp[i]=-2000000000;
29         dp[0]=0; a[0]=-2000000000;
30         for(int i=1;i<=n;i++){
31             for(int j=i-1;j>=0;j--){
32                 if(a[i]>=a[j]){
33                     dp[i]=max(dp[i],dp[j]-sigma(1,i-j-1)+a[i]);
34                 }
35             }
36         }
37         int ans=-2000000000;
38         // for(int i=1;i<=n;i++) cout<<dp[i]<<" ";cout<<endl;
39         for(int i=1;i<=n;i++){
40             ans=max(ans,dp[i]-sigma(1,n-i));
41         } write(ans);
42         return 0;
43     }
44 }
45 signed main(){return WSN::main();}
View Code

 

然后正解很多,有用李超线段树的,还可以用数状数组,还可以$CDQ$分治,我用的$CDQ$分治,跟打怪差不多

只要是把方程式化成斜率式即可,打法可以参考打怪

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 namespace AE86{
 5     inline int read(){
 6         int x=0,f=1;char ch=getchar();
 7         while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 8         while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;
 9     }inline void write(int x,char opt='\n'){
10         char ch[20];int len=0;if(x<0)x=~x+1,putchar('-');
11         do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
12         for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);}
13 }using namespace AE86;
14 
15 const int NN=3e5+5,inf=0x3fffffff;
16 int n,ans[NN];
17 struct SNOW{
18     int a,x,y,dp;
19     SNOW(){dp=-inf;}
20 }p[NN];
21 inline bool cmp1(SNOW a,SNOW b){return a.a==b.a?a.x<b.x:a.a<b.a;}
22 inline bool cmp2(SNOW a,SNOW b){return a.x<b.x;}
23 inline double slope(int x,int y){return 1.0*(p[y].y-p[x].y)/(p[y].x-p[x].x);}
24 inline int sigma(int n){return (1+n)*n/2;}
25 int q[NN];
26 inline void merge_sort(int l,int r){
27     if(l>=r) return; int mid=(l+r)>>1;
28     merge_sort(l,mid);
29     sort(p+l,p+mid+1,cmp2); sort(p+mid+1,p+r+1,cmp2);
30     int h=1,t=0,j=l;
31     for(int i=mid+1;i<=r;i++){
32         while(j<=mid && p[j].x<=p[i].x){
33             while(h<t && slope(q[t],q[t-1])<=slope(q[t-1],j)) --t;
34             q[++t]=j; j++;
35         }
36         while(h<t && slope(q[h+1],q[h])>=-p[i].x) ++h;
37         if(h>t) continue;
38         ans[p[i].x]=p[i].dp=max(p[i].dp,p[q[h]].y+p[i].x*p[q[h]].x-sigma(p[i].x-1)+p[i].a);
39         p[i].y=p[i].dp-sigma(p[i].x);
40     }
41     sort(p+l,p+r+1,cmp1);
42     merge_sort(mid+1,r);
43 }
44 
45 namespace WSN{
46     inline short main(){
47         freopen("skip.in","r",stdin);
48         freopen("skip.out","w",stdout);
49         n=read(); memset(ans,-0x3f,sizeof(ans));
50         for(int i=1;i<=n;i++){
51             p[i].a=read(); p[i].x=i;
52             ans[i]=p[i].dp=p[i].a-sigma(i-1);
53             p[i].y=p[i].dp-sigma(p[i].x);
54         } sort(p+1,p+n+1,cmp1);
55         merge_sort(1,n);
56         for(int i=1;i<n;i++) ans[n]=max(ans[n],ans[i]-sigma(n-i));
57         write(ans[n]);
58         return 0;
59     }
60 }
61 signed main(){return WSN::main();}
View Code

 

T2 string

惊人的数据范围,考场上看到就跳了,但是只要找到优化方法就没那么变态了

发现在k特别大的时候可以把它消除掉,会是非常有规律的$abababa...abacdcdcdcd...cdcefefefef...efe$

然后可以缩小字符集,还可以缩小长度,这样会把长度缩小到$8$,然后状压记忆化

详细参考学长博客(因为我是学习的学长博客,而且是在9.19)

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 int k,n,s,sta,tim[30],a[30],r;
 5 char ans[10];
 6 unordered_map<int,int> mp[10];
 7 inline int get(int x){return x?1<<4*x-4:0;}
 8 inline int dfs(int len,int S,int rk,int last){
 9     if(mp[last].find(S)!=mp[last].end() && rk>=mp[last][S]) return mp[last][S];
10     if(S==sta){
11         if(!rk){puts(ans); exit(0);}
12         return mp[last][S]=1;
13     }
14     int tmp=0,r=0;
15     for(int ch=s;ch<26;ch++) if(ch+'a'!=ans[len-1]){
16         ans[len]=ch+'a'; tim[ch]++; a[tim[ch]]++;
17         if(a[tim[ch]]<=k-tim[ch]+1){
18             r=dfs(len+1,S+get(tim[ch])-get(tim[ch]-1),rk,tim[ch]);
19             tmp+=r; rk-=r;
20         }
21         --a[tim[ch]]; --tim[ch];
22     } return mp[last][S]=tmp;
23 }
24 namespace WSN{
25     inline short main(){
26         freopen("string.in","r",stdin);
27         freopen("string.out","w",stdout);
28         scanf("%lld%lld",&k,&n);
29         while(k>8){
30             for(int i=1;i<k;i++) putchar(s+'a'), putchar(s+'b');
31             putchar(s+'a'); k-=2; s+=2;
32         }
33         for(int i=1;i<=k;i++) sta|=get(i);
34         dfs(0,0,n-1,9); puts("-1");
35         return 0;
36     }
37 }
38 signed main(){return WSN::main();}
View Code

 

T3 permutation

可喜可贺,无法拿到首$A$但是拿到了第二滴血,毕竟首$A$是沈队

但是这题确实做了有够久,今天主要精力说这道题。。。

考场上一看是数学,就开始刚,刚到最后,不过有好的结果,就是$70$分

怎么来的? 打表!!!!

用打表来治数学题确实不错,但是这东西不能依赖,只是骗分的工具

但是这道题可以用打表干到满分

意外意外。。。。。。

先打$10$分暴力,列出组合后发现有一种神奇的层递关系,且与$k,m$的取值密切相关

大概是看他的每一列输出,都是类似$4,3,2,1,3,2,1,2,1,1$的东西,且和他前面那一列包含,前面那一列又是一个更长的循环

然后按照列是k,行是m打出一张表:

    9     8     7     6     5     4     3     2     1     0 
   81    64    49    36    25    16     9     4     1     0 
  321   232   161   106    65    36    17     6     1     0 
  981   652   413   246   135    66    27     8     1     0 
 2565  1576   917   498   247   108    39    10     1     0 
 5997  3424  1841   918   415   164    53    12     1     0 
12861  6856  3425  1578   655   236    69    14     1     0 
25731 12862  5999  2568   985   326    87    16     1     0 
48611 22872 10003  3998  1425   436   107    18     1     0 
87507 38888 16009  6000  1997   568   129    20     1     0 

(附加打表程序)

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 namespace AE86{
 5     inline int read(){
 6         int x=0,f=1;char ch=getchar();
 7         while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 8         while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;
 9     }inline void write(int x,char opt='\n'){
10         char ch[20];int len=0;if(x<0)x=~x+1,putchar('-');
11         do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
12         for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);}
13 }using namespace AE86;
14 
15 const int NN=3e3+5,mod=1e9+7;
16 int n,m,k;
17 int biao[NN][NN];
18 int precha[NN];
19 int A[6005][4005],tmp;
20 vector<int> pai;
21 inline void dfs(int st,int cnt){
22     if(cnt==k){
23         ++tmp;
24         for(int i=0;i<pai.size();i++) A[tmp][i+1]=pai[i];
25         return;
26     }
27     for(int i=st+1;i<=n;i++){
28         pai.push_back(i);
29         dfs(i,cnt+1);
30         pai.pop_back();
31     }
32 }
33 namespace WSN{
34     inline short main(){
35         freopen("perm.in","r",stdin);
36         freopen("perm.out","w",stdout);
37         n=read(); k=read(); m=read();
38         if(n<=3){
39             dfs(0,0);
40             int ans=0;
41             for(int i=1;i<tmp;i++)
42                 (ans+=abs(A[i][m]-A[i+1][m]))%=mod;
43             write(ans);
44             return 0;
45         }
46         for(int i=1;i<=n;++i)
47             biao[i][n]=0, biao[i][n-1]=1, biao[i][n-2]=2*i, precha[i]=2;
48         for(int j=n-3;j;j--){
49             biao[1][j]=n-j; int cha=biao[1][j]*(n-j-1)%mod;
50             for(int i=2;i<=n;i++){
51                 biao[i][j]=(biao[i-1][j]+cha)%mod;
52                 cha=(cha+precha[i-1])%mod; precha[i-1]=cha;
53             }
54         }
55         write(biao[m][k]);
56         // for(int i=1;i<=n;i++){
57         //     for(int j=1;j<=n;j++){
58         //         printf("%5lld ",biao[i][j]);
59         //     } cout<<endl;
60         // }
61         return 0;
62     }
63 }
64 signed main(){return WSN::main();}
RE 70(打表)

不难发现他是一个高阶等差数列(按照每一列来看,公差数列分别是$0,0,0$,$2,2,2$,$6,8,10,12$,$12,20,..$)

每一列的公差的公差是上一列的公差,$O(n^2)$打表就有$70$

但是后来考后推发现这个高阶等差无法推出正解

于是我们用这张表找另一个规律(不对,换一张表):

  __ 0__1__2__3__4__5    /n-k-1
0|    1    0    0    0    0    0
1|    0    1    2    3    4    5
2|    0    2    4    6    8    10
3|    0    3    9    17  27  39
4|    0    4    16  36  66  108
/m

行列关系如下,就是把刚才那张表倒一个个儿(行列没太对齐,是那个意思)

精心的找规律可以发现,他是有一个递推公式的设$f_{i,j}$表示$i$行$j$列的答案

发现:$f_{i,j}=f_{i-1,j}+f_{i,j-1}+j$,然后按照这个公式打,会有$70$分的$TLE$

考虑这个公式很像原来做的工业题

当时考场上也是干出了工业题的正解,因为行列打反痛失$A$题机会,印象深刻,所以就想到了

考虑公式意义,也是重要的题目转化:

你需要从$(i,j)$这个点走到$(n,m)$这个点,每次可以选择向下或者向右走一步,并花费你所在列编号个贡献。

求走到$(n,m)$这个点的总贡献。然后这道题的答案就变为了求$f_{m,n-k-1}$的答案。

可以直接使用组合数求出方案然后乘上贡献即可,注意在$m=1$这一行没有这个规律,但是他们可以直接算出组合数不乘贡献(或理解为贡献为1)

这样可以打出可以优化到正解的$70$分$TLE$做法:

 

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 namespace AE86{
 5     inline int read(){
 6         int x=0,f=1;char ch=getchar();
 7         while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 8         while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;
 9     }inline void write(int x,char opt='\n'){
10         char ch[20];int len=0;if(x<0)x=~x+1,putchar('-');
11         do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
12         for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);}
13 }using namespace AE86;
14 
15 const int NN=1e6+5,mod=1e9+7;
16 int n,m,k,ans;
17 int h[NN],v[NN];
18 inline int qmo(int a,int b){
19     int ans=1,c=mod; a%=c;
20     while(b){
21         if(b&1) ans=ans*a%c;
22         b>>=1; a=a*a%c;
23     } return ans;
24 }
25 inline void pre(){
26     h[0]=h[1]=1; v[0]=v[1]=1;
27     for(int i=2;i<NN;i++) h[i]=h[i-1]*i%mod;
28     v[NN-1]=qmo(h[NN-1],mod-2);
29     for(int i=NN-2;i>=2;i--) v[i]=v[i+1]*(i+1)%mod;
30 }
31 inline int C(int n,int m){
32     if(n<m||n<0||m<0) return 0;
33     return h[n]*v[n-m]%mod*v[m]%mod;
34 }
35 namespace WSN{
36     inline short main(){
37         freopen("perm.in","r",stdin);
38         freopen("perm.out","w",stdout);
39         n=read(); k=read(); m=read(); pre();
40         n-=k+1;
41         for(int i=1;i<=m;i++){
42             for(int j=0;j<=n;j++){
43                 ans=(ans+(i==1?1:j)*C(n-j+m-i,m-i)%mod)%mod;
44             }
45         }
46         write(ans);
47         return 0;
48     }
49 }
50 signed main(){return WSN::main();}
TLE 70

 

然后对于同一列的组合数贡献一样,那么可以使用前缀和优化,因为组合数在杨辉三角同一列的前缀和为其末尾右下角的值

$\sum_{i=m}^{n}C_{i}^{m}=C_{n+1}^{m+1}$,这样每一列的不包括$m=1$的直接处理,然后$m=1$的再处理一遍,总共$O(n)$扫两边

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 namespace AE86{
 5     inline int read(){
 6         int x=0,f=1;char ch=getchar();
 7         while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 8         while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;
 9     }inline void write(int x,char opt='\n'){
10         char ch[20];int len=0;if(x<0)x=~x+1,putchar('-');
11         do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
12         for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);}
13 }using namespace AE86;
14 
15 const int NN=1e6+5,mod=1e9+7;
16 int n,m,k,ans;
17 int h[NN],v[NN];
18 inline int qmo(int a,int b){
19     int ans=1,c=mod; a%=c;
20     while(b){
21         if(b&1) ans=ans*a%c;
22         b>>=1; a=a*a%c;
23     } return ans;
24 }
25 inline void pre(){
26     h[0]=h[1]=1; v[0]=v[1]=1;
27     for(int i=2;i<NN;i++) h[i]=h[i-1]*i%mod;
28     v[NN-1]=qmo(h[NN-1],mod-2);
29     for(int i=NN-2;i>=2;i--) v[i]=v[i+1]*(i+1)%mod;
30 }
31 inline int C(int n,int m){
32     if(n<m||n<0||m<0) return 0;
33     return h[n]*v[n-m]%mod*v[m]%mod;
34 }
35 namespace WSN{
36     inline short main(){
37         freopen("perm.in","r",stdin);
38         freopen("perm.out","w",stdout);
39         n=read(); k=read(); m=read();
40         if(n==k) return puts("0"),0; pre(); n-=k+1;
41         if(m>=2) for(int i=0;i<=n;i++) ans=(ans+(n-i)*C(m+i-1,m-2)%mod)%mod;
42         for(int i=0;i<=n;i++) ans=(ans+C(m+i-1,m-1))%mod;//小于2的特判,不符合规律
43         write(ans);
44         return 0;
45     }
46 }
47 signed main(){return WSN::main();}
View Code

然后正解可以去看:

ZXS

T4 小P的生成树

 

 

 

 把题解上面的$tan$换一下(因为他反了),剩下的找他的做就可以了,思路只要是见过这类题型就可以记住

 

 1 #include<bits/stdc++.h>
 2 #define pii pair<double,double>
 3 #define mp make_pair
 4 #define fi first
 5 #define se second
 6 #define int long long
 7 using namespace std;
 8 namespace AE86{
 9     inline int read(){
10         int x=0,f=1;char ch=getchar();
11         while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12         while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;
13     }inline void write(int x,char opt='\n'){
14         char ch[20];int len=0;if(x<0)x=~x+1,putchar('-');
15         do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
16         for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);}
17 }using namespace AE86;
18 
19 const int NN=55,MM=205;
20 int n,m,fa[NN],cnt;
21 double si[80000],ans;
22 struct node{int u,v,x,y;double val;}p[MM];
23 inline double Tan(int yi,int er){
24     return 1.0*(p[er].x-p[yi].x)/(p[er].y-p[yi].y);
25 }
26 inline pii Sita(int yi,int er){
27     return mp(1.0*atan(Tan(yi,er)),1.0*atan(Tan(yi,er))+3.1415926);
28 }
29 inline int getfa(int x){return fa[x]=((fa[x]==x)?x:getfa(fa[x]));}
30 inline bool cmp(node a,node b){
31     return a.val>b.val;
32 }
33 inline void krus(){
34     int seg=0; sort(p+1,p+m+1,cmp);
35     double A=0.0,B=0.0;
36     for(int i=1;i<=n;i++) fa[i]=i;
37     for(int i=1;i<=m;i++){
38         int u=p[i].u,v=p[i].v;
39         if(getfa(u)!=getfa(v)){
40             fa[getfa(u)]=getfa(v);
41             ++seg; A+=p[i].x; B+=p[i].y;
42         }
43         if(seg==n-1) break;
44     } ans=max(ans,(double)sqrt(1.0*A*A+1.0*B*B));
45 }
46 namespace WSN{
47     inline short main(){
48         freopen("mst.in","r",stdin);
49         freopen("mst.out","w",stdout);
50         n=read(); m=read();
51         for(int i=1;i<=m;i++){
52             int u=read(),v=read(),a=read(),b=read();
53             p[i]=(node){u,v,a,b,0.0};
54         }
55         for(int i=1;i<m;i++) for(int j=i+1;j<=m;j++){
56             pii now=Sita(i,j); si[++cnt]=now.fi; si[++cnt]=now.se;
57         } si[++cnt]=-1.5707964; si[++cnt]=4.7123891;
58         sort(si+1,si+cnt+1);
59         for(int i=2;i<=cnt;i++){
60             double jia=(si[i]+si[i-1])/2.0;
61             for(int j=1;j<=m;j++){
62                 p[j].val=(double)(1.0*p[j].x*cos(jia)+1.0*p[j].y*sin(jia));
63             } krus();
64         }
65         double jia=(si[cnt]+si[1])/2.0;
66         for(int j=1;j<=m;j++){
67             p[j].val=(double)(1.0*p[j].x*cos(jia)+1.0*p[j].y*sin(jia));
68         } krus();
69         printf("%.6lf\n",ans);
70         return 0;
71     }
72 }
73 signed main(){return WSN::main();}
View Code

 

posted @ 2021-09-19 21:15  雪域亡魂  阅读(115)  评论(0编辑  收藏  举报