Bestcoder #39
2015-05-03 16:16:11
总结:这场... 前面两题挺快,卡第三题了TAT。
A题 hdu 5210:贪心题。
删k个数字,使得最后剩下的不同的数尽可能多。
水题... 多扫几遍乱搞下就行。
#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 MEM(a,b) memset(a,b,sizeof(a)) #define REP(i,n) for(int i=0;i<(n);++i) #define FOR(i,a,b) for(int i=(a);i<=(b);++i) #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; int v[105]; int main(){ while(scanf("%d",&n) != EOF){ MEM(v,0); for(int i = 1; i <= n; ++i){ int a; scanf("%d",&a); v[a]++; } int k; scanf("%d",&k); for(int i = 1; i <= n; ++i){ while(k && v[i] > 1){ v[i]--; k--; } if(k == 0) break; } for(int i = 1; i <= n; ++i) if(v[i]){ if(k > 0){ v[i]--; k--; } if(k == 0) break; } int cnt = 0; for(int i = 1; i <= n; ++i) if(v[i]) cnt++; printf("%d\n",cnt); } return 0; }
B题 hdu 5211:筛法。
对于每个数 a[i] ,找到一个 a[ki] (i < ki <= n)使得 a[i] | a[k],累加所有 ki。
开计数数组,从左到右扫描,类似筛法,检查每个数的约数,累加,然后把计算过的赋值为0。
#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 MEM(a,b) memset(a,b,sizeof(a)) #define REP(i,n) for(int i=0;i<(n);++i) #define FOR(i,a,b) for(int i=(a);i<=(b);++i) #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; int v[10010]; int vis[10010]; int main(){ while(scanf("%d",&n) != EOF){ for(int i = 1; i <= n; ++i) scanf("%d",&v[i]); MEM(vis,0); ll ans = 0; for(int i = 1; i <= n; ++i){ int top = sqrt(1.0 * v[i]); for(int j = 1; j <= top; ++j) if(v[i] % j == 0){ //j and v[i] / j if(vis[j]){ ans += vis[j] * i; vis[j] = 0; } if(j == 1) continue; int tmp = v[i] / j; if(vis[tmp]){ ans += vis[tmp] * i; vis[tmp] = 0; } } vis[v[i]]++; } cout << ans << endl; } return 0; }
C题 hdu 5212:容斥+筛法。
对于 N 个数里面的所有数对 (a[i] , a[j])(有顺序性),累加 gcd(a[i],a[j]) * (gcd(a[i],a[j]) - 1)。
题解比较高端... 其实很简单...
从大到小枚举 gcd ,然后用类似筛法的方法找出这个 gcd 的倍数的个数 k,那么这 k 个数的配对方式就有 k^2。
注意到这些倍数中可能有已经计算过的 2×gcd 的倍数,所以要减掉 2×gcd 的倍数的个数,那么这就像一种容斥了。
综合一下,用 num 表示一个数的倍数的个数,我们得到当前答案为 num(gcd) - num(2×gcd) - num(3×gcd) - .... - num(p×gcd) ,p×gcd <= max
#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 MEM(a,b) memset(a,b,sizeof(a)) #define REP(i,n) for(int i=0;i<(n);++i) #define FOR(i,a,b) for(int i=(a);i<=(b);++i) #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 ll mod = 10007; int n; int v[10010]; ll num[100010]; int main(){ int a; while(scanf("%d",&n) != EOF){ MEM(v,0); MEM(num,0); ll ans = 0; for(int i = 1; i <= n; ++i){ scanf("%d",&a); v[a]++; } for(int i = 1; i <= 10000; ++i){ for(int j = i; j <= 10000; j += i) num[i] += v[j]; } for(int i = 10000; i >= 2; --i){ ll cur = num[i] * num[i] % mod; for(int j = i + i; j <= 10000; j += i){ cur = (cur - num[j] + mod) % mod; } num[i] = cur; ans = (ans + (ll)i * (i - 1) * num[i]) % mod; } cout << ans << endl; } return 0; }
D题 hdu 5213:莫队+容斥。
给出 N 个数(数的范围:[1,N])以及常数 K,然后给出 M 个询问,每个询问四个数 l1,r1,l2,r2,求出 a[i] + a[j] == K 的方案数,其中 l1<=i<=r1 , l2<=j<=r2
比较明显的莫队题,但是如果不会容斥转化还是不好做的...
把每个询问的答案定义为:F(A,B) (A,B表示区间)
由于每个询问涉及两个区间,莫队不方便直接做,所以努力将其转化为单区间询问问题,于是容斥应运而生~
列出式子:F(A,B) = F(A+B+C,A+B+C) - F(A+C,A+C) - F(B+C,B+C) + F(C,C)
那么只要把每个询问的两个区间拆分成四个区间来做即可~
(小吐槽:用C++ wa了,用G++ ac了,还快了不少,有点醉.... >.<)
#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; const int MAXN = 30010; int N,K,M,block; int A[MAXN]; int num[MAXN]; ll cur_val; struct Query{ int l,r,id,bid; ll ans; }q[MAXN << 2]; void Update(int l,int r,int d){ for(int i = l; i <= r; ++i){ num[A[i]] += d; if(A[i] < K) cur_val += num[K - A[i]] * d; } } bool cmp(Query a,Query b){ return a.bid == b.bid ? a.r < b.r : a.bid < b.bid; } bool cmp_id(Query a,Query b){ return a.id < b.id; } void Solve(){ cur_val = 0; memset(num,0,sizeof(num)); int tM = M << 2; sort(q + 1,q + tM + 1,cmp); for(int i = 1,l = 1,r = 0; i <= tM; ++i){ if(q[i].l < l) Update(q[i].l,l - 1,1); if(l < q[i].l) Update(l,q[i].l - 1,-1); if(q[i].r < r) Update(q[i].r + 1,r,-1); if(r < q[i].r) Update(r + 1,q[i].r,1); q[i].ans = cur_val; l = q[i].l; r = q[i].r; } sort(q + 1,q + tM + 1,cmp_id); for(int i = 1; i <= tM; i += 4){ printf("%I64d\n",q[i].ans - q[i+1].ans - q[i+2].ans + q[i+3].ans); } } int main(){ int l1,r1,l2,r2; while(scanf("%d",&N) != EOF){ block = (int)sqrt(1.0 * N); scanf("%d",&K); for(int i = 1; i <= N; ++i) scanf("%d",&A[i]); scanf("%d",&M); for(int i = 1; i <= M; ++i){ scanf("%d%d%d%d",&l1,&r1,&l2,&r2); int cur = (i - 1) * 4 + 1; q[cur].bid = l1/block , q[cur].l = l1 , q[cur].r = r2; q[cur+1].bid = l1/block , q[cur+1].l = l1 , q[cur+1].r = l2-1; q[cur+2].bid = (r1+1)/block , q[cur+2].l = r1+1 , q[cur+2].r = r2; q[cur+3].bid = (r1+1)/block , q[cur+3].l = r1+1 , q[cur+3].r = l2-1; q[cur].id = cur; q[cur+1].id = cur+1; q[cur+2].id = cur+2; q[cur+3].id = cur+3; } Solve(); } return 0; }