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 }
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 }
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 }
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 }