Codeforces #311 div2
2015-07-14 12:50:34
【传送门】
总结:前4题都不难。最后一次涉及字符串,待补...
A题、B题不说了。
C题:排序,枚举
题意:有一个n个脚的桌子,每根脚有长度和拆除它的需要的能量,为了使桌子平衡,你要拆除一些脚。平衡的条件是最长的脚的数量 > k / 2(k为剩余的脚的总数量),要求求出所需的最少总能量。(n <= 10^5 ,脚的长度 <= 10^5 ,拆除脚需要的能量 <= 200)
思路:首先将所有脚按照长度进行排序,然后枚举最后剩下的脚中的最大长度,那么显然比枚举值长的脚要拆除,因为还要使得桌子平衡,所以先统计出有多少脚的长度等于枚举的长度,设其数量为cnt,先拆除比枚举长度更长的脚,然后统计剩下的脚的数量M,那么还需要拆除的脚的数量就是ned = max(0,M-(2*cnt-1)),当然我们要找出所需能量最少的ned个脚,然后将其拆除。找的过程可以这样:由于能量的范围很小,我们可以预先统计出每个能量有多少根脚,然后动态维护这个数组,找的时候从小到大遍历即可。
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 100010; int n; int D[MAXN]; pii P[MAXN]; int main(){ scanf("%d",&n); for(int i = 1; i <= n; ++i){ scanf("%d",&P[i].first); } for(int i = 1; i <= n; ++i){ scanf("%d",&P[i].second); D[P[i].second]++; } sort(P + 1,P + n + 1); int sum = 0; int ans = INF; for(int i = n; i >= 1; ){ int cnt = 0; int tmpsum = 0; for(int j = i; j >= 1 && P[j].first == P[i].first; --j,++cnt){ D[P[j].second]--; tmpsum += P[j].second; } int ned = max(0,i - cnt * 2 + 1); int curcnt = 0,cursum = 0; for(int j = 1; j <= 200; ++j){ if(curcnt + D[j] >= ned){ cursum += (ned - curcnt) * j; ans = min(ans,sum + cursum); break; } else{ curcnt += D[j]; cursum += D[j] * j; } } sum += tmpsum; i -= cnt; } printf("%d\n",ans); return 0; }
(吐槽:一开始没看到能量的范围,当成10^5来做了,然后就用了树状数组+二分的写法,如下)
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> using namespace std; const int MAXN = 100010; const long long mod = 1e8 + 7; int n; struct node{ int l,d; }p[MAXN]; struct BIT{ int c[MAXN]; inline int lowbit(int x){ return x & (-x); } void update(int x,int d){ while(x < MAXN){ c[x] += d; x += lowbit(x); } } int getsum(int x){ int res = 0; while(x){ res += c[x]; x -= lowbit(x); } return res; } }bit1,bit2; bool cmp(node a,node b){ if(a.l == b.l) return a.d < b.d; return a.l < b.l; } int main(){ scanf("%d",&n); for(int i = 1; i <= n; ++i){ scanf("%d",&p[i].l); } for(int i = 1; i <= n; ++i){ scanf("%d",&p[i].d); bit1.update(p[i].d,1); bit2.update(p[i].d,p[i].d); } sort(p + 1,p + n + 1,cmp); int sum = 0,cursum = 0,m = n,curcnt = 0,ans = 1 << 30; for(int i = n; i >= 1; --i){ cursum += p[i].d; curcnt++; m--; bit1.update(p[i].d,-1); bit2.update(p[i].d,-p[i].d); if(i == 1 || p[i].l != p[i - 1].l){ //考虑这一块 int ned = (m + curcnt) - (curcnt * 2 - 1); if(ned <= 0){ ans = min(ans,sum); } else{ int l = 1,r = 100000; while(l < r){ int mid = (l + r) / 2; if(bit1.getsum(mid) >= ned) r = mid; else l = mid + 1; } int tcnt = bit1.getsum(l - 1); int tsum = bit2.getsum(l - 1) + (ned - tcnt) * l; ans = min(ans,tsum + sum); } sum += cursum; curcnt = cursum = 0; } } printf("%d\n",ans); return 0; }
D题:DFS(黑白染色),组合数
题意:给出一个n个点,m条边的无向图,问你最少加多少条边使得图中出现奇环(长度>1,且为奇数),以及方案数。
思路:这种奇偶环的问题很自然想到黑白染色,首先DFS将图黑白染色掉,然后我们发现,给同个连通块内同色的点之间连边就能构造出奇环,那么就好办了,对于每个连通块统计出黑点数cnt1和白点数cnt2,然后组合数搞下即可,方案数:C(cnt1,2) + C(cnt2,2)。
以上是只用加一条边的情况,还有另外两种情况:(1)加两条条边,这要求每个连通块内黑白点个数均<=1,且m!=0(必须要有边存在),这时每条边都是独立的,不与其他边相邻,那么加边必定以某条边为基础,再选边端点之外的n-2个点中的一个连两条边,所以方案数:m×(n-2)
(2)加三条边,这时要求m=0(没有边存在),方案数就是C(n,3)
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 1000010; int n,m; int first[MAXN],ecnt; int vis[MAXN],col[MAXN]; int flag,cnt0,cnt1; struct edge{ int v,next; }e[MAXN << 1]; inline void add_edge(int u,int v){ e[ecnt].next = first[u]; e[ecnt].v = v; first[u] = ecnt++; } void Dfs(int p,int c){ if(!flag) return; col[p] = c; vis[p] = 1; if(c) cnt1++; else cnt0++; for(int i = first[p]; ~i; i = e[i].next){ int v = e[i].v; if(vis[v] == 0) Dfs(v,c ^ 1); else if(col[v] == c){ flag = 0; return; } } } int main(){ int a,b; memset(first,-1,sizeof(first)); ecnt = 0; scanf("%d%d",&n,&m); for(int i = 1; i <= m; ++i){ scanf("%d%d",&a,&b); add_edge(a,b); add_edge(b,a); } if(n <= 2){ printf("0 1\n"); return 0; } ll ans = INF,anscnt = 0; flag = 1; for(int i = 1; i <= n; ++i) if(vis[i] == 0){ cnt0 = cnt1 = 0; Dfs(i,0); if(cnt0 >= 2){ if(ans != 1){ ans = 1; anscnt = 0; } anscnt += (ll)cnt0 * (cnt0 - 1) / 2; } if(cnt1 >= 2){ if(ans != 1){ ans = 1; anscnt = 0; } anscnt += (ll)cnt1 * (cnt1 - 1) / 2; } if(!flag) break; } if(!flag){ printf("0 1\n"); return 0; } if(ans == INF){ if(m == 0){ printf("3 %I64d\n",(ll)n * (n - 1) * (n - 2) / 6); } else{ printf("2 %I64d\n",(ll)m * (n - 2)); } } else{ printf("%I64d %I64d\n",ans,anscnt); } return 0; }