寒假集训好题记录

Day3B CF1012C(DP)

题目大意:给你一个序列,定义第i个位置为山峰当且仅当h[i-1]和h[i+1]均小于h[i],现在可以减少某些h[i]的值,代价为减少的量的总和。对于1到n/2(向上取整)的每个数j,需要求出当序列里至少有j个山峰时,需要付出的最小代价

考场再次弱智,我想个**的贪心,直接大力DP,再前缀和优化就行了

定义f[i][j]为第i个是山峰,前i个点总共放了j个山峰的最小花费,那么i成为山峰需要协调h[i-1]和h[i+1]的值

根据贪心的策略,如果h[i+1]大于等于h[i],那么我们一定把h[i+1]调整为h[i]-1。

有了这个结论,我们就能确定当i-2是山峰时,h[i-1]被调整后的值,进而用于f[i]的转移

直接DP的n3的,又由于第i-1个位置不与小于等于i-3的位置相关联,那么i-3之前的状态可以用前缀最大值优化!

优化成了n2,轻松跑过

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define ll long long
 5 using namespace std;
 6 const int N1=2505;
 7 const int inf=0x3f3f3f3f;
 8 
 9 int n,m,ns;
10 int a[N1*2],f[N1*2][N1],g[N1];
11 
12 int main()
13 {
14     scanf("%d",&n);
15     for(int i=1;i<=n;i++) scanf("%d",&a[i]); 
16     if(n==1){
17         puts("0"); return 0;
18     }
19     memset(f,0x3f,sizeof(f)); memset(g,0x3f,sizeof(g));
20     f[0][0]=0; f[1][0]=0; a[0]=a[1]+1;
21     int ma;
22     if(a[1]>a[2]) f[1][1]=0; else f[1][1]=a[2]-a[1]+1;
23     for(int i=2;i<=n;i++)
24     {
25         ma=(i&1)?i/2+1:i/2;
26         if(i==5)
27             n=n;
28         f[i][1]=max(0,a[i-1]-a[i]+1)+max(0,a[i+1]-a[i]+1);
29         for(int j=2;j<=ma;j++)
30         {
31             if(a[i-1]<a[i-2]){
32                 f[i][j]=f[i-2][j-1]+max(0,a[i-1]-a[i]+1);
33             }else{
34                 f[i][j]=f[i-2][j-1]+max(0,(a[i-2]-1)-a[i]+1);
35             }
36             if(i-3>=0)
37             {
38                 f[i][j]=min(f[i][j],g[j-1]+max(0,a[i-1]-a[i]+1));
39             }
40             if(i<n) f[i][j]+=max(0,a[i+1]-a[i]+1);
41         }
42         for(int j=1;j<=ma;j++) g[j]=min(g[j],f[i-2][j]);
43     }
44     for(int j=1;j<=ma;j++)
45     {
46         g[j]=min(g[j],min(f[n-1][j],f[n][j]));
47         printf("%d ",g[j]);
48     }
49     puts("");
50     return 0;
51 }
View Code

 

Day3D CF862D(构造)

妙妙题目

解题必然从分治入手

考虑先把0或1其中一个给找出来,然后再用分治的办法找另一个。

前两次,问00……0和00……1,就能确定最后一位是0还是1了,现在假设最后一位是0,那么我们还需要找到1

记录00……0的答案是x。再进行构造,1~mid是0,mid+1~r是1,记录这个询问序列的答案是y

假设前后0和1的数量分别为l0,r0,l1,r1。

那么x=l1+r1,y=l1+r0,又由于r0+r1=r-mid,因此可求出l1的值!

根据l1的值决定向左区间或者右区间递归求解,最后一定能找到1

注意如果不是第一层,询问还会带上非递归区间的贡献,用最初的x消一下就行了

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define ll long long
 5 using namespace std;
 6 const int N1=1005;
 7 const int inf=0x3f3f3f3f;
 8 
 9 int n,pos0,pos1;
10 char str[N1];
11 
12 int l0,r0,l1,r1,x,y;
13 void dfs1(int sum1,int l,int r)
14 {
15     if(l==r){ pos1=l; return; }
16     int mid=(l+r)>>1,sum2,l1;
17     for(int i=1;i<=n;i++) str[i]='0';
18     for(int i=l;i<=mid;i++) str[i]='0';
19     for(int i=mid+1;i<=r;i++) str[i]='1'; str[n+1]='\n';
20     printf("? %s",str+1); fflush(stdout);
21     scanf("%d",&sum2); sum2=sum2-(x-sum1);
22     l1=sum1+sum2-(r-mid); l1/=2;
23     if(l1>0) dfs1(l1,l,mid);
24     else dfs1(sum1,mid+1,r);
25 }
26 
27 void dfs2(int sum1,int l,int r)
28 {
29     if(l==r){ pos0=l; return; }
30     int mid=(l+r)>>1,sum2,l1;
31     for(int i=1;i<=n;i++) str[i]='1';
32     for(int i=l;i<=mid;i++) str[i]='1';
33     for(int i=mid+1;i<=r;i++) str[i]='0'; str[n+1]='\n';
34     printf("? %s",str+1); fflush(stdout);
35     scanf("%d",&sum2); sum2=sum2-(x-sum1);
36     l1=sum1+sum2-(r-mid); l1/=2;
37     if(l1>0) dfs2(l1,l,mid);
38     else dfs2(sum1,mid+1,r);
39 }
40 
41 int main()
42 {
43     // freopen("a.in","r",stdin);
44     scanf("%d",&n);
45     for(int i=1;i<=n;i++) str[i]='0'; str[n+1]='\n';
46     printf("? %s",str+1); fflush(stdout);
47     scanf("%d",&x); 
48     str[n]='1';
49     printf("? %s",str+1); fflush(stdout);
50     scanf("%d",&y); 
51     if(x<y){
52         pos0=n; 
53         dfs1(x,1,n-1);
54     }else{
55         pos1=n;
56         for(int i=1;i<=n;i++) str[i]='1'; str[n+1]='\n';
57         printf("? %s",str+1); fflush(stdout);
58         scanf("%d",&x); 
59         dfs2(x,1,n-1);
60     }
61     printf("! %d %d\n",pos0,pos1);
62     return 0;
63 }
View Code

 

Day3H CF1060D(贪心)

题目大意:n个人,围成任意个闭环,对于每个人,要求他左手和右手边分别至少有l[i]和r[i]个空凳子。如果一个人围成环,那么他左右两边是交叠在一起的。现在问最少需要的凳子数量

把每个人的左右手分开来看,如果我们给每个左手分配一个独特的右手,那么左手和右手构成一一映射。放到图上,这一方法可以理解为一条边只能被两个人分别用左侧和右侧用,比较显然地成立了

那么如何分配是最优策略呢?考虑对左手右手分别排序,那么把第i个左手和第i个右手相对应是最优的。

考虑交换两个左手/右手,代价一定比原来大……

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define ll long long
 5 using namespace std;
 6 const int N1=100005;
 7 const int inf=0x3f3f3f3f;
 8 
 9 int n,m;
10 int l[N1],r[N1];
11 
12 int main()
13 {
14     scanf("%d",&n);
15     int x,y,v; ll ans=0;
16     for(int i=1;i<=n;i++) 
17     {
18         scanf("%d%d",&x,&y);
19         if(x==y) ans+=x;
20         else m++, l[m]=x, r[m]=y;
21     }
22     sort(l+1,l+m+1); sort(r+1,r+m+1);
23     for(int i=1;i<=m;i++) ans+=max(l[i],r[i]);
24     printf("%lld\n",ans+n);
25     return 0;
26 }
View Code

 

Day3I CF1060E(树上计数)

奇数长度路径变为x/2+1,偶数长度路径变为x/2

最终答案=(所有路径总长度-变化前奇数路径总数目)/2

总长度在树上数就行,奇数长度的路径数目呢?

dis(x,y)=dep[x]+dep[y]-2*dep[lca(x,y)]

减掉的lca不影响奇偶性!因此直接根据dep数组就能求出这个数目了

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define ll long long
 5 using namespace std;
 6 const int N1=200005;
 7 const int inf=0x3f3f3f3f;
 8 
 9 int n;
10 int sz[N1],dep[N1];
11 ll sdis[N1],sum[N1];
12 struct Edge{
13 int to[N1*2],nxt[N1*2],head[N1],cte;
14 void ae(int u,int v)
15 { cte++; to[cte]=v; nxt[cte]=head[u]; head[u]=cte; }
16 }e;
17 
18 ll dfs(int u,int ff)
19 {
20     ll ans=0; sz[u]=1;
21     for(int j=e.head[u];j;j=e.nxt[j])
22     {
23         int v=e.to[j]; if(v==ff) continue;
24         dep[v]=dep[u]+1; ans+=dfs(v,u);
25         ans+=(sdis[u]+sz[u])*sz[v]+sdis[v]*sz[u];
26         sz[u]+=sz[v]; sdis[u]+=sdis[v]+sz[v];
27     }
28     return ans;
29 }
30 
31 int main()
32 {
33     scanf("%d",&n);
34     int x,y; 
35     for(int i=1;i<n;i++) scanf("%d%d",&x,&y), e.ae(x,y), e.ae(y,x);
36     ll ans=dfs(1,0),s0=0,s1=0,tot=0;
37     for(int i=1;i<=n;i++) 
38     {
39         sum[dep[i]]++;
40         if(dep[i]&1) s1++; else s0++;
41     }
42     for(int i=0;i<n;i++)
43     {
44         if(i&1) tot+=1ll*sum[i]*s0;
45         else tot+=1ll*sum[i]*s1;
46     }
47     tot>>=1;
48     ans=(ans+tot)>>1;
49     printf("%lld\n",ans);
50     return 0;
51 }
View Code

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章