Codeforces #307 div2
2015-06-14 17:13:05
【传送门】
总结:比赛的时候有点逗比... C wa了一发后弃疗了,过掉了D,最后D因为一个巨坑的数据FST掉了... QAQ
A题:简单排序
#include <cstdio> #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; int n; struct node{ int a,id,pos; }p[2010]; bool cmp(node a,node b){ return a.a > b.a; } bool cmp_id(node a,node b){ return a.id < b.id; } int main(){ scanf("%d",&n); for(int i = 1; i <= n; ++i){ scanf("%d",&p[i].a); p[i].id = i; } sort(p + 1,p + n + 1,cmp); int P = 0; p[1].pos = 1; for(int i = 2; i <= n; ++i){ if(p[i].a == p[i - 1].a){ p[i].pos = p[i - 1].pos; } else p[i].pos = i; } sort(p + 1,p + n + 1,cmp_id); for(int i = 1; i <= n; ++i){ printf("%d ",p[i].pos); } puts(""); return 0; }
B题:枚举
思路:因为字母可以随便交换,没有次数限制,所以先统计出A串中各个字母的出现次数,然后枚举交换位置后的A串包含几个B串,然后在每次枚举时算出此时最多能包含多少个C串,计算出最大值即可。
#include <cstdio> #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; char a[100010],b[100010],c[100010]; int cnt[300],cntx[300],cntb[300],cntc[300]; int len1,len2,len3; bool Check(int m){ for(int i = 0; i < 300; ++i) if(cntb[i]){ if(cnt[i] < cntb[i] * m) return false; } return true; } int Cal(int m){ int tmin = INF; memcpy(cntx,cnt,sizeof(cnt)); for(int i = 0; i < 300; ++i) if(cntb[i]){ cntx[i] -= cntb[i] * m; } for(int i = 0; i < 300; ++i) if(cntc[i]){ tmin = min(tmin,cntx[i] / cntc[i]); } return tmin; } int main(){ scanf("%s%s%s",a,b,c); len1 = strlen(a); len2 = strlen(b); len3 = strlen(c); for(int i = 0; i < len1; ++i){ cnt[a[i]]++; } for(int i = 0; i < len2; ++i){ cntb[b[i]]++; } for(int i = 0; i < len3; ++i){ cntc[c[i]]++; } int ans = 0,ansx = -1,ansy = -1; int top = len1 / len2; for(int i = 0; i <= top; ++i){ if(Check(i)){ int v = Cal(i); if(i + v > ans){ ans = i + v; ansx = i; ansy = v; } } } if(ans == 0){ printf("%s\n",a); return 0; } else{ for(int i = 0; i < ansx; ++i) printf("%s",b); for(int i = 0; i < ansy; ++i) printf("%s",c); memcpy(cntx,cnt,sizeof(cnt)); for(int i = 0; i < len2; ++i) cntx[b[i]] -= ansx; for(int i = 0; i < len3; ++i) cntx[c[i]] -= ansy; for(int i = 0; i < 300; ++i) if(cntx[i]){ for(int j = 0; j < cntx[i]; ++j) printf("%c",i); } puts(""); } return 0; }
C题:二分
题意:有n堆盒子,第i堆有ai个盒子,现在有m个人在第1堆的左边,每一秒每个人可以走向右边的堆或者搬掉当前堆的一个盒子,问最少时间搬完。
思路:二分答案,然后倒着考虑,首先考虑最后一堆,在当前枚举的时间下,计算最少要派多少个人到最后一堆才能搬完,式子表达为
num = ceil(a[n] / (cur_time - n)) ceil表示向上取整,,我们发现如果式子不是整除的话,有些人会有空闲的情况,那么我们可以让这些人在来到第 n堆之前拿前面一些堆的盒子,这个值为:remain = num * (cur_time - n) - a[n]。这样我们就获得了一种贪心算法,从第n堆开始考虑到第1堆,不断累加 remain 值,如当前堆比 remain 值大,那么减掉再算出需要的人数,然后更新 remain 值。
#include <cstdio> #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; int n,m; ll A[100010]; bool Check(ll v){ ll tm = m,rem = 0; for(int i = n; i >= 1; --i) if(A[i]){ if(A[i] <= rem){ rem -= A[i]; continue; } ll tA = A[i] - rem; if(v - i <= 0) return false; ll ned = (ll)ceil(1.0 * tA / (v - i)); rem = ned * (v - i) - tA; tm -= ned; if(tm < 0) return false; } return true; } int main(){ scanf("%d%d",&n,&m); ll sum = 0; for(int i = 1; i <= n; ++i){ scanf("%I64d",A + i); } ll l = 0,r = 1LL << 60; while(l < r){ ll mid = getmid(l,r); if(Check(mid)) r = mid; else l = mid + 1; } printf("%I64d\n",l); return 0; }
D题:数学统计,快速计算Fib / 矩阵快速幂
题意:有n个数,a1、a2~an,每个数的范围为[0~2^l),问有多少种n个数的数列满足: 这个条件,答案对 m 取模。
思路:独立地考虑 k 的每一个二进制位,如果为1,那么必定存在某两个相邻的数在这一位均为1;如果为0,则反之。
【用dp可以计算出对于某个二进制位,不存在任意两个相邻的数在该位均为1的情况数。dp[i][j] 表示第i个数在该位为j,那么就有转移公式:
,答案为 ,我们发现这个答案就是第 n+2 个斐波那契数!
由于 n 很大,所以可以用矩阵快速幂解决,就不赘述了。当然还可以用斐波那契另外一个公式:
所以:,这样就可以用记忆化搜索了!】
最后,只要逐位考虑 k 的每个二进制位,如果是1,答案乘上 2^n - F(n+2);如果是0,答案乘上F(n+2) 。
#include <cstdio> #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; ll n,k,l,m,sum; map<ll,ll> fib; ll Fib(ll v){ if(fib.count(v)) return fib[v]; ll k = v / 2; if(v & 1) fib[v] = (Fib(k+1)*Fib(k+1)%m + Fib(k)*Fib(k)%m) % m; else fib[v] = (Fib(k+1)*Fib(k)%m + Fib(k)*Fib(k-1)%m) % m; } ll Qpow(ll x,ll y){ ll res = 1; while(y){ if(y & 1) res = res * x % m; x = x * x % m; y >>= 1; } return res; } int main(){ scanf("%I64d%I64d%I64d%I64d",&n,&k,&l,&m); fib[0] = fib[1] = 1; sum = Fib(n + 2); ll ans = 1; for(int i = l; i <= 63; ++i){ if(k & (1LL << i)){ ans = 0; break; } } if(ans == 0){ printf("0\n"); return 0; } ll top = Qpow(2,n); for(int i = 0; i < l; ++i){ ll v = 1LL << i; if(k & v){ ans *= (top - sum + m); ans %= m; } else ans = (ans * sum) % m; } printf("%I64d\n",ans % m); return 0; }
E题:分块,排序
题意:给出n个数的数列(n <= 5*10^5),有q个查询(q <= 5*10^4),有两种查询,第一种是给一个区间加上一个x,第二种查询给出 y ,输出最大 j - i,
其中 i 和 j 满足 ai = aj = y 。
思路:将 n 个数分成若干块,每块 sqrt(n)个数,然后快内排序。开一个数组 plus[] 用来保存每个块整块的增加值,表示块内所有元素都要加上这个值。
对于第一种查询,考虑区间涉及的所有块,如果这块完全包含于区间,则给该块的 plus 值加上x;否则暴力更改块内被包含在区间内的数,然后重新排序
(这个过程往往被称为块内重建)。
对于第二种查询,逐个考虑每一块,用二分查找找出下标最大的 y 值和下标最小的 y 值,这就要求将每个数保存成 pair 类型,first 为数值,second 为下标。
下标最大的 y 值可以用 upper_bound()位置减1 处理,下标最小的 y 值可以用 lower_bound 处理。(具体看程序)就这样维护出 y 的最小下标和 y 的最大标答案就是最大下标 - 最小下标。
#include <cstdio> #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 push_back typedef long long ll; typedef pair<int,int> pii; typedef pair<long long,int> pli; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 500010; const int MAX_SQR = 1000; int n,q,block,bid[MAXN]; vector<pli > A[MAX_SQR]; ll val[MAXN],PL[MAX_SQR]; int L[MAX_SQR],R[MAX_SQR]; inline bool cmp(pli a,pli b){ if(a.first == b.first) return a.second < b.second; return a.first < b.first; } inline void Rebuild(int id){ A[id].clear(); for(int i = L[id]; i <= R[id]; ++i) A[id].PB(MP(val[i],i)); sort(A[id].begin(),A[id].end(),cmp); } int main(){ int a,b,c,d; scanf("%d%d",&n,&q); block = (int)sqrt(1.0 * n); //每块长度 int bcnt = n / block + (n % block ? 1 : 0); for(int i = 1; i <= n; ++i){ scanf("%I64d",&val[i]); bid[i] = (i - 1) / block + 1; A[bid[i]].PB(MP(val[i],i)); } for(int i = 1; i <= bcnt; ++i){ //计算边界 L[i] = (i - 1) * block + 1; R[i] = i * block; sort(A[i].begin(),A[i].end()); } for(int i = 1; i <= q; ++i){ scanf("%d",&a); if(a == 1){ scanf("%d%d%d",&b,&c,&d); int sid = (b - 1) / block + 1,eid = (c - 1) / block + 1; //首末块的编号 if(sid == eid){ for(int j = b; j <= c; ++j) val[j] += d; Rebuild(sid); } else{ if(b > L[sid]){ for(int j = b; j <= R[sid]; ++j) val[j] += d; Rebuild(sid++); } if(c < R[eid]){ for(int j = L[eid]; j <= c; ++j) val[j] += d; Rebuild(eid--); } for(int j = sid; j <= eid; ++j) PL[j] += d; } } else{ scanf("%d",&b); int pmax = -1,pmin = INF; for(int j = 1; j <= bcnt; ++j){ ll cur = b - PL[j]; int num = lower_bound(A[j].begin(),A[j].end(),MP(cur,0)) - A[j].begin(); int pos = A[j][num].second; if(A[j][num].first == cur && num < A[j].size()){ pmin = min(pmin,pos); num = upper_bound(A[j].begin(),A[j].end(),MP(cur,INF)) - A[j].begin() - 1; pos = A[j][num].second; //printf("%d : pos2 %d\n",j,pos); pmax = max(pmax,pos); } } if(pmax == -1) printf("-1\n"); else printf("%d\n",pmax - pmin); } } return 0; }