Loading

Noip模拟43 2021.8.18

T1 地一体

可以树形$dp$,但考场没写出来,只打了没正确性的贪心水了$30$

然后讲题的时候B哥讲了如何正确的贪心,喜出望外的学习了一下

不难发现

每次士兵都会直接冲到叶子节点

从深的点再返回到另一个比较浅的点肯定是不优的

只有两种情况,士兵从之前的点到新的节点与直接再安排一个士兵冲到这个节点

我们就按这个策略贪心即可

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 #define pb push_back
 4 #define mp make_pair
 5 #define pii pair<int,int>
 6 #define fi first
 7 #define se second
 8 using namespace std;
 9 namespace AE86{
10     inline int read(){
11         int x=0,f=1;char ch=getchar();
12         while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
13         while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;
14     }inline void write(int x,char opt='\n'){
15         char ch[20];int len=0;if(x<0)x=~x+1,putchar('-');
16         do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
17         for(int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);}
18 }using namespace AE86;
19 
20 const int NN=1e5+5,inf=0x3fffffff;
21 int n,ans,pos;
22 int fa[NN],dep[NN],mdp[NN];
23 bool vis[NN];
24 vector<pii > e[NN];
25 
26 inline void dfs1(int f,int x){
27     fa[x]=f; mdp[x]=dep[x];
28     for(int i=0;i<e[x].size();i++){
29         int y=e[x][i].se; if(y==f) continue;
30         dep[y]=dep[x]+1;
31         dfs1(x,y);
32         e[x][i].fi=mdp[y];
33         mdp[x]=max(mdp[x],mdp[y]);
34     }
35 }
36 inline void calc(int x){
37     if(!pos){
38         pos=x,ans+=dep[pos];
39     }else{
40         if(dep[x]<dep[x]+dep[pos]-2*dep[fa[x]]) pos=x,ans+=dep[x];
41         else ans+=dep[x]+dep[pos]-2*dep[fa[x]],pos=x;
42     }
43 }
44 inline void dfs(int f,int x){
45     if(!vis[x]) calc(x),vis[x]=1;
46     for(int i=0;i<e[x].size();i++){
47         int y=e[x][i].se; if(y==f) continue;
48         dfs(x,y);
49     }
50 }
51 
52 namespace WSN{
53     inline short main(){
54         n=read();
55         for(int i=1;i<n;i++){
56             int x=read(),y=read();
57             e[x].pb(mp(0,y)); e[y].pb(mp(0,x));
58         }dfs1(0,1);//for(int i=1;i<=n;i++) cout<<dep[i]<<endl;
59         for(int i=1;i<=n;i++) sort(e[i].begin(),e[i].end());
60         dfs(0,1); write(ans);
61         return 0;
62     }
63 }
64 signed main(){return WSN::main();}
View Code

 

T2 滴而提

不难发现可以二分出答案

然后考虑在二分的时候如何判断,

宗旨是这样的,如果能更新得动就更新,大神叫这个(迭代最终必将收敛)

一个限制是$K$,如果没有加的次数了就不行了,就收敛就行了

另一个限制是有差值,我们考虑每一个小的$id(i,j)$都必须被填到和周围的格子一样大

这个就是更新,那么如果没有可以更新的就收敛就行

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 #define mp make_pair
 4 #define pii pair<int,int>
 5 #define fi first
 6 #define se second
 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(int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);}
17 }using namespace AE86;
18 
19 const int NN=1e5+5;
20 int n,m,k,a[NN],tmp[NN];
21 inline int id(int x,int y){return m*(x-1)+y;}
22 inline bool check(int mid){
23     int K=k;    bool f=1;
24     for(int i=1;i<=n*m;i++) tmp[i]=a[i];
25     while(K>=0&&f){
26         f=0;
27         for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){
28             if(i-1>=1){
29                 if(tmp[id(i-1,j)]-tmp[id(i,j)]>mid){ f=1;
30                     int res=tmp[id(i-1,j)]-tmp[id(i,j)];
31                     tmp[id(i,j)]+=res-mid; K-=res-mid;
32                 }if(K<0) return 0;
33             }
34             if(i+1<=n){
35                 if(tmp[id(i+1,j)]-tmp[id(i,j)]>mid){ f=1;
36                     int res=tmp[id(i+1,j)]-tmp[id(i,j)];
37                     tmp[id(i,j)]+=res-mid; K-=res-mid;
38                 }if(K<0) return 0;
39             }
40             if(j-1>=1){
41                 if(tmp[id(i,j-1)]-tmp[id(i,j)]>mid){ f=1;
42                     int res=tmp[id(i,j-1)]-tmp[id(i,j)];
43                     tmp[id(i,j)]+=res-mid; K-=res-mid;
44                 }if(K<0) return 0;
45             }
46             if(j+1<=m){
47                 if(tmp[id(i,j+1)]-tmp[id(i,j)]>mid){ f=1;
48                     int res=tmp[id(i,j+1)]-tmp[id(i,j)];
49                     tmp[id(i,j)]+=res-mid; K-=res-mid;
50                 }if(K<0) return 0;
51             }
52             if(i+1<=n&&j-1>=1){
53                 if(tmp[id(i+1,j-1)]-tmp[id(i,j)]>mid){ f=1;
54                     int res=tmp[id(i+1,j-1)]-tmp[id(i,j)];
55                     tmp[id(i,j)]+=res-mid; K-=res-mid;
56                 }if(K<0) return 0;
57             }
58             if(i-1>=1&&j+1<=m){
59                 if(tmp[id(i-1,j+1)]-tmp[id(i,j)]>mid){ f=1;
60                     int res=tmp[id(i-1,j+1)]-tmp[id(i,j)];
61                     tmp[id(i,j)]+=res-mid; K-=res-mid;
62                 }if(K<0) return 0;
63             }
64         }
65     }
66     return 1;
67 }
68 
69 namespace WSN{
70     inline short main(){
71         n=read();m=read();k=read();int l=0,r=0,ans=l;
72         for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[id(i,j)]=read();
73         for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){
74             int dis=0,d1=0,d2=0,d3=0,d4=0,d5=0,d6=0;
75             if(i+1<=n) d1=abs(a[id(i,j)]-a[id(i+1,j)]);
76             if(i-1>=1) d2=abs(a[id(i,j)]-a[id(i-1,j)]);
77             if(j+1<=m) d3=abs(a[id(i,j)]-a[id(i,j+1)]);
78             if(j-1>=1) d4=abs(a[id(i,j)]-a[id(i,j-1)]);
79             if(i+1<=n&&j-1>=1) d5=abs(a[id(i,j)]-a[id(i+1,j-1)]);
80             if(i-1>=1&&j+1<=m) d6=abs(a[id(i,j)]-a[id(i-1,j+1)]);
81             dis=max(d1,max(d2,max(d3,max(d4,max(d5,d6)))));
82             if(r<dis) r=dis;
83         }
84         while(l<=r){
85             int mid=l+r>>1;
86             if(check(mid)) r=mid-1,ans=mid;
87             else l=mid+1;
88         }write(ans);
89         return 0;
90     }
91 }
92 signed main(){return WSN::main();}
View Code

 

T3 帝三踢

还没改出来,沽沽沽

 

T4 迪斯鶙

高爸给黄队讲昊爸的做法(集训队的辈份问题是道$NPC$。。。):

可以神仙的把 每个好的序列里面一个数$x$出现次数的平方$k^2$拆成$k^2=\binom{k}{2}*2+k$

然后试图用$dp$表示这个柿子,首先预处理出两个$dp$数组

$f_{i,j}$表示选了$i$个数,最大的值是$j$的方案数

$g_{i,j}$表示前面的一段序列最大值是$j$,后面选了$i$个数的方案数

然后我们就可以把上面的柿子变成

$(\sum_{y>=x} f_{i-1,y}*(g_{n-i,y}+2*(n-i)*g_{n-i-1,y}))+f_{i-1,x-1}*(g_{n-i,x}+2*(n-i-1)*g_{n-i-1,x})$

只能说太神了,先再理解了半天才彻底搞明白。。。

最后统计答案的时候处理一个前缀和数组就行

 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(int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);}
13 }using namespace AE86;
14 
15 const int N=3e3+5;
16 int n,p,f[N][N],g[N][N],dp[N][N],ans[N];
17 
18 namespace WSN{
19     inline short main(){
20         n=read();p=read();f[0][0]=1;
21         for(int i=0;i<=n;i++) g[0][i]=1;
22         for(int i=1;i<=n;i++) for(int j=1;j<=i;j++)
23             f[i][j]=(f[i-1][j-1]+f[i-1][j]*j%p)%p;
24         for(int i=1;i<=n;i++) for(int j=1;j<=n-i;j++)
25             g[i][j]=(g[i-1][j+1]+g[i-1][j]*j%p)%p;
26         for(int i=1;i<=n;i++){
27             for(int x=n;x;x--){
28                 dp[i][x]=(dp[i][x+1]+f[i-1][x]*(g[n-i][x]+2*(n-i)%p*g[n-i-1][x]%p)%p)%p;
29                 (ans[x]+=dp[i][x]+f[i-1][x-1]*(g[n-i][x]+2*(n-i)%p*g[n-i-1][x]%p)%p)%=p;
30             }
31         }for(int i=1;i<=n;i++) write(ans[i],' ');
32         return 0;
33     }
34 }
35 signed main(){return WSN::main();}
View Code

 

posted @ 2021-08-18 19:34  雪域亡魂  阅读(100)  评论(2编辑  收藏  举报