寒假集訓好題記錄

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

 

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