《第十五届中北大学算法与程序设计竞赛(公开赛)》
K:
思路:
最优的操作就是一直操作一个数。
证明:
设最后的结果为
(a[i]+xi)-(a[j]-yj).
= abs(a[i]-a[j])+xi+yj (x+y = m);因为i,j肯定不一样。
那么max(i,j)x+max(i,j)y >= xi+yj.
所以全部分给下标最大的那个更优.
所以只需要去枚举操作哪个数,然后操作这个数为最大时和最小时都和最大值最小值计算下即可。
Code:
LL a[N]; int main() { int n,m;sdd(n,m); LL minn = INF,maxx = -INF; for(int i=1;i<=n;++i) { sld(a[i]); maxx = max(maxx,a[i]); minn = min(minn,a[i]); } if(n == 1) printf("0\n"); else { LL ans = -INF; for(int i=1;i<=n;++i) { LL ma1 = a[i]+m*i; LL tmp = ma1-minn; ans = max(ans,tmp); LL ma2 = a[i]-m*i; tmp = maxx-ma2; ans = max(ans,tmp); } plr(ans); } system("pause"); return 0; }
C:
思路:差分+贪心
以第一个数为基准,让所有数向它靠近.
假设差分数组为b.
那么我们要使数组后面的数都和a[1]一样。
那么就是让b数组都为0(除了b[1]).
那么如何让这个b都为0最优。
我们通过差分这个差分数组的思想。
对于一段区间[L,r]。中间的数都为0.
我们可以让这个区间内的数都+1,那么区间内的差分值都不会变化.
就是b[L]++,b[r+1]--.同理都-1,b[L]--,b[r+1]--.
那么我们可以进行min(abs(b[L]),abs(b[r]))次操作,让区间[L,r]的一个端点变成0.
然后我们继续去找区间。那么很显然这样的区间两个端点是符号相反的。
然后可以思考,当一段区间的一端归0后,另一端也相对的减去了min(abs(b[L]),abs(b[r])).
然后我们继续去找符号相反的。最后其实就是让所有的负值和正值中较少的全归0,然后剩下的都对自己进行+1,-1操作来归0.
那么我们只需要去统计差分数组的正数个数和负数个数即可(除b[1]外).
那么显然答案就是min(num1,num2)+abs(num1-num2)
不难发现这个值其实就是max(num1,num2)
更直观地感受
假设差分数组b为
1 2 -3 4 5 -6 1.
第一次[2,3]进行min(abs(2),abs(3)) = 2次-1操作变成
1 0 -1 4 5 -6 1.
第二次[3,4]进行min(abs(-1),abs(4)) = 1次+1操作变成
1 0 0 3 5 -6 1.
第三次[5,6]进行min(abs(5),abs(-6)) = 5次-1操作变成
1 0 0 3 0 -1 1.
第四次[6,7]进行min(abs(-1),abs(1)) = 1次+1操作变成
1 0 0 3 0 0 0.
第五次[4,4]进行3次-1变成
1 0 0 0 0 0 0.
over!(注意会爆int)
Code:
int a[N]; int main() { int n;sd(n); LL ma1 = 0,ma2 = 0; for(int i=1;i<=n;++i) { sd(a[i]); if(i == 1) continue; LL tmp = a[i]-a[i-1]; if(tmp > 0) ma1 += tmp; else ma2 -= tmp;//减负数其实就是加 } LL ans = max(ma1,ma2); plr(ans); system("pause"); return 0; } F: 思路: 将在线查询改为离线查询. 用set来维护离散化后的元素和元素的值+1. 一开始我们让所有元素都在set中 很显然这个>=的值可以二分找到。 如果第一个数为a[1],那么a[1]+1就是他的大于1. 第二个数为a[2].那么如果a[1]+1 != a[2]. 那么这时候要的值就是a[2],如果a[1]+1 > a[2]. 这时的值就是a[2]+1. 以此类推,当a[2]+1 = a[3]时就是a[3]+1. 所以将所有的值和他们的+1都放入集合。 这个大于等于的值具有传递性。所以可以二分找。 因为我们维护的集合是不在原来的集合内的,所以我们插入这个数就要从这个集合中删去。 删去这个数就要把它插入到集合中 Code: struct Node{int id,x;}p[N]; int main() { int n;sd(n); set<int> S; for(int i=1;i<=n;++i) { sdd(p[i].id,p[i].x); S.insert(p[i].x); S.insert(p[i].x+1); } for(int i=1;i<=n;++i) { if(p[i].id == 1) S.erase(p[i].x); if(p[i].id == 2) S.insert(p[i].x); if(p[i].id == 3) { auto v = S.lower_bound(p[i].x); pr(*v); } } //system("pause"); return 0; } H:记忆化搜索 思路: 基于底层开始统计的记忆化搜索 Code: LL dp[55][1005][10][10];//dp[n][sum][i][j]表示n位数时,和为sum且前两位为i,j时的个数 int n,sum,x; LL dfs(int pos,int num,int pre1,int pre2) { if(pos == n) { if(num == sum) return 1; return 0; } if(num > sum) return 0;//剪枝 if(dp[pos][num][pre1][pre2] != -1) return dp[pos][num][pre1][pre2]; LL ans = 0; for(int i=0;i<10;++i) { if((pos < 2) || (pre2*100+pre1*10+i)%x == 0) ans = (ans+dfs(pos+1,num+i,i,pre1))%Mod; } return dp[pos][num][pre1][pre2] = ans; } int main() { metf(dp); sddd(n,sum,x); LL ans = dfs(0,0,0,0); plr(ans); //system("pause"); return 0; }
I:二分图最大权匹配
因为x+y = 奇
那么只有奇和偶的配的才能满足。
所以可以看成奇和偶的二分图。
然后就是二分图最大权匹配的模板了。
注意点:
1.保证去配对的一方都是最小的那组数。这样才能走出循环。
2.这里的单个情况不需要算入结果。
3.因为任意奇偶之间都能配对,所以少的那一方肯定全都配对完,所以总对数就是min(cnt1,cnt2).
Code:
//1-奇,2-偶,奇匹配偶 //保证cnt1 < cnt2.且a-cnt1,b-cnt2. int vis1[N],vis2[N],n,cnt1 = 0,cnt2 = 0; LL cost1[N],cost2[N],dis[N][N],slack[N],a[N],b[N],match[N]; bool Find(int x) { vis1[x] = 1; for(int i=1;i<=cnt2;++i) { if(vis2[i]) continue; LL tmp = cost1[x]+cost2[i]-dis[x][i]; if(tmp == 0) { vis2[i] = 1; if(match[i] == -1 || Find(match[i])) { match[i] = x; return true; } } else slack[i] = min(slack[i],tmp); } return false; } LL km() { met0(cost2);metf(match); for(int i=1;i<=cnt1;++i) for(int j=1;j<=cnt2;++j) cost1[i] = max(cost1[i],dis[i][j]); for(int i=1;i<=cnt1;++i) { for(int j=1;j<=cnt2;++j) slack[j] = INF; while(1) { met0(vis1);met0(vis2); if(Find(i)) break; LL d = INF; for(int j=1;j<=cnt2;++j) if(!vis2[j]) d = min(d,slack[j]); for(int j=1;j<=cnt1;++j) if(vis1[j]) cost1[j] -= d; for(int j=1;j<=cnt2;++j) { if(vis2[j]) cost2[j] += d; else slack[j] -= d; } } } LL ans = 0; for(int i=1;i<=cnt2;++i) { if(match[i] != -1) ans += dis[match[i]][i]; // else ans += b[i]; } return ans; } int main() { sd(n); for(int i=1;i<=n;++i) { LL x;sld(x); if(x&1) a[++cnt1] = x; else b[++cnt2] = x; } if(cnt1 > cnt2) { swap(cnt1,cnt2); swap(a,b); } for(int i=1;i<=cnt1;++i) for(int j=1;j<=cnt2;++j) dis[i][j] = a[i]^b[j]; LL ans = km(); printf("%d %lld\n",min(cnt1,cnt2),ans); // system("pause"); return 0; }