Codeforces Round #232 (Div. 1)

这次运气比较好,做出两题。本来是冲着第3题可以cdq分治做的,却没想出来,明天再想好了。

A. On Number of Decompositions into Multipliers

题意:n个数a1,a2, a3...an求n个数相乘与a1*a2*a3*a4...an相等的排列个数。

分析:首先应该对ai分解质因数,求出所有ai中质因数及个数,枚举排列中每个数包含的质因数个数,例如质因数qi,有ni个,相应的排列中数包含质因数qi个数设为x1,x2,....xn, x1+x2+x3..+xn = ni , 那么对于qi共有C(ni+n-1, n-1)种情况。简单来说就是将ni分成n部分,这样想:有ni个球,需要分成n部分,也就是在n个球中插入n-1根木棍,这样分成n部分就相当于ni+n-1中选n-1个位置。最后把所有质因子可能的划分情况相乘起来就行了。

注意:分解质因数最后一个质因数可能很大!

代码:

 1 #include <bits/stdc++.h>
 2 #define in freopen("solve_in.txt", "r", stdin);
 3 #define bug(x) printf("Line %d : >>>>>>>\n", (x));
 4 #define pb push_back
 5 
 6 using namespace std;
 7 typedef long long LL;
 8 typedef map<int, int> Mps;
 9 const int M = (int)1e9+7;
10 const int maxn = 555;
11 const int maxm = (int)1e5 + 100;
12 LL inv[maxn];
13 int a[maxn];
14 Mps Div;
15 vector<int> dv;
16 
17 LL powmod(LL a, LL b) {
18     LL res = 1;
19     while(b) {
20         if(b&1)
21             res = (res*a)%M;
22         b >>= 1;
23         a = (a*a)%M;
24     }
25     return res;
26 }
27 void getInv(int n) {
28     for(int i = 1; i < n; i++) {
29         inv[i] = powmod(i, M-2);
30     }
31 }
32 void getDiv(int x) {
33     int m = (int)sqrt(x+.5);
34     for(int i = 2; i <= m; i++) {
35         if(x%i == 0) {
36             if(Div[i] == 0)
37                 dv.pb(i);
38             while(x%i == 0) {
39                 Div[i]++;
40                 x/=i;
41             }
42         }
43     }
44     if(x > 1) {
45         if(Div[x] == 0)
46             dv.pb(x);
47         Div[x]++;
48     }
49 }
50 LL nCr(LL n, LL m) {
51     LL res = 1;
52     for(int i = 0; i < m; i++) {
53         res = res*(n-i)%M*inv[i+1]%M;
54         if(res < 0)
55             res += M;
56     }
57     return (res + M)%M;
58 }
59 int main() {
60 
61     getInv(maxn);
62     int n;
63     scanf("%d", &n);
64     for(int i = 1; i <= n; i++) {
65         scanf("%d", a+i);
66         getDiv(a[i]);
67     }
68     LL ans = 1;
69     for(int i = 0; i < (int)dv.size(); i++) {
70         int x = dv[i];
71         ans = ans*nCr(Div[x]+n-1, n-1)%M;
72     }
73     cout<<ans<<endl;
74     return 0;
75 }
View Code

B. On Sum of Fractions

题意:给定n, u(i)表示不超过i的最大质数, v(i)表示大于i的最小质数。求对于所有的2<= i <= n , sum{1/(u(i)*v(i)}, 最简分式结果。

分析:

列出n的前几个数的1/u(i)*v(i),发现对于两个相邻素数pi, pi+1间的数i, 1/u(i)*v(i)结果是一样的。也就是说 对于  pi <= x   < pi+1, sum{1/u(x)v(x)},化简后就是, 1/u(x)-1/v(x), 得到这个结论后,每次找到大于n的一个素数pi+1,结果就变成两部分, x < pi, 和 pi <= x < n, 第一部分化简就是1/2-1/pi ,第二部分:n-pi+1/(pi*pi+1), 两者之和合并一下就会得到一个表达式:2*(n+1)-2*(pi+pi+1)-pi*pi+1/(2*pi*pi+1)。求质数,用Miller-Rabin法判断前后两个质数就可以了。

代码:

 1 #include <bits/stdc++.h>
 2 #define in freopen("solve_in.txt", "r", stdin);
 3 #define bug(x) printf("Line %d : >>>>>>>\n", (x));
 4 #define pb push_back
 5 
 6 using namespace std;
 7 typedef pair<int, int> PII;
 8 typedef long long LL;
 9 typedef map<int, int> Mps;
10 
11 LL powmod(LL a, LL b, LL c) {
12     LL res = 1;
13     while(b) {
14         if(b&1) res = res*a%c;
15         b >>= 1;
16         a = (a*a)%c;
17     }
18     return res;
19 }
20 bool test(int n, int a, int d) {
21     if(n == 2)return true;
22     if(n == a) return true;
23     if((n&1) == 0) return false;
24     while(!(d&1)) d >>= 1;
25     int t = powmod(a, d, n);
26     while((d != n-1) && (t != 1) && (t != n-1)) {
27         t = (LL)t*t%n;
28         d <<= 1;
29     }
30     return (t == n-1) || (d&1) == 1;
31 
32 }
33 bool isPrime(int n) {
34     if(n < 2) return false;
35     int a[] = {2, 3, 61};
36     for(int i = 0; i <= 2; i++) if(!test(n, a[i], n-1)) return false;
37     return true;
38 }
39 pair<LL, LL> getPrime(LL n) {
40     LL x = n;
41     x++;
42     PII ans;
43     while(1) {
44         if(isPrime(x)) {
45             ans.first = x;
46             break;
47         }
48         x++;
49     }
50     x = n;
51     while(1) {
52         if(isPrime(x)) {
53             ans.second = x;
54             break;
55         }
56         x--;
57     }
58     return ans;
59 }
60 LL getGcd(LL a, LL b) {
61     return !b ? a : getGcd(b, a%b);
62 }
63 int main() {
64 
65     int T;
66     int n;
67     for(int t = scanf("%d", &T); t <= T; t++) {
68         scanf("%d", &n);
69         if(n == 2) {
70             puts("1/6");
71         } else {
72             PII u = getPrime(n);
73             LL x = u.first, y = u.second;
74             LL a = 2*(n+1)-2*(x+y)+x*y;
75             LL b = 2*x*y;
76             LL c = getGcd(a, b);
77             printf("%I64d/%I64d\n", a/c, b/c);
78         }
79     }
80     return 0;
81 }
View Code

C. On Changing Tree

题意:一棵n个结点的树,n <= 3*10^5, 给定q个操作。两种类型:

1 v x k ,给v及v子树上结点增加x-i*k,i表示子树上离v的距离

2 v,询问v结点上的值。

分析:

经过一上午的使劲YY,终于想到了CDQ分治的做法,其实比普通树状数组做法要慢,而且难写(也可能是我写得太挫了),复杂度最坏可能达到O(n*log^2n).1200ms能过也算是走运。

简要思路:首先将所有操作按照v结点的深度由大到小拍个序,具体分治的时候,对于原始编号在[l,r]的操作,要将它分成两部分,<= m 和>m 部分,然后考虑编号<=m,的修改操作(操作1) 对编号>m的询问操作(操作2)影响,其实也就是用操作1对所有影响的的要询问的结点的值更新。但是又不能针对每一<=m的操作1,去更新可能影响到的>m操作2, 这样复杂度会很高,那么问题关键就在于怎么按照深度的顺序将操作1作用累积起来,每次更新对结点v影响时,只需要考虑v到根节点的路径上最靠近v的一个操作1。

 

合并两个同一棵子树但是不同深度的操作1的影响时,例如1 v1 x1 k1, 1 v2 x2 k2, dep[v1] <= dep[v2],将v1合并到v2时,新的x =  x1 - (dep[v2]-dep[v1])*k1 + x2, 新的k = k1+k2。同时在询问和更新某个结点v的上方且最靠近的v的操作1对应的结点时是利用线段树维护的,具体是将子树对应的叶节点一一编号,然后看做区间,将对应结点更新时,只需要将该节点的子树包含的叶子节点的区间进行更新,例如v对应叶子节点编号[L,R]只需要将一个标记cover设置为v,表示该段叶子结点对应的子树的根为最新的。这样做的依据是树中每棵子树包含的叶子结点都不同,但是可以连续编号的。

说的有点复杂,还是普通方法简单,这个也只是为了练习CDQ分治。||--

代码:

 

  1 /*Time 2014 09 01 , 10:22
  2 
  3 */
  4 #include <bits/stdc++.h>
  5 #define in freopen("solve_in.txt", "r", stdin);
  6 #define bug(x) printf("Line %d : >>>>>>>\n", (x));
  7 #define lson rt<<1, l, m
  8 #define rson rt<<1|1, m+1, r
  9 #define inf 0x0f0f0f0f
 10 #define pb push_back
 11 
 12 using namespace std;
 13 typedef long long LL;
 14 const int maxn = 3*(int)1e5 + 100;
 15 const int M = (int)1e9 + 7;
 16 vector<int> g[maxn];
 17 int ll[maxn], rr[maxn], dep[maxn], rk[maxn], t1[maxn], t2[maxn];
 18 int cover[maxn<<2];
 19 int ans[maxn], x[maxn], k[maxn];
 20 int qN;
 21 int cnt, ct, vis[maxn];
 22 
 23 struct Query
 24 {
 25     int type;
 26     int v, x, k;
 27 
 28 } qu[maxn];
 29 bool cmp(const int &a, const int &b)
 30 {
 31     return dep[qu[a].v] < dep[qu[b].v];
 32 }
 33 void dfs(int u)
 34 {
 35     if(g[u].size() == 0)
 36     {
 37         ll[u] = rr[u] = ++cnt;
 38         return;
 39     }
 40     else ll[u] = inf, rr[u] = 0;
 41     for(int i = 0; i < g[u].size(); i++)
 42     {
 43         int v = g[u][i];
 44         dep[v] = dep[u]+1;
 45         dfs(v);
 46         ll[u] = min(ll[u], ll[v]);
 47         rr[u] = max(rr[u], rr[v]);
 48     }
 49 }
 50 void pushDown(int rt)
 51 {
 52     if(cover[rt] != -1)
 53     {
 54         cover[rt<<1] = cover[rt<<1|1] = cover[rt];
 55         cover[rt] = -1;
 56     }
 57 }
 58 void update(int rt, int l, int r, int L, int R, int u)
 59 {
 60     if(L <= l && R >= r)
 61     {
 62         cover[rt] = u;
 63         return;
 64     }
 65     pushDown(rt);
 66     int m = (l+r)>>1;
 67     if(L <= m)
 68     update(lson, L, R, u);
 69     if(R > m)
 70     update(rson, L, R, u);
 71 }
 72 void query(int rt, int l, int r, int L, int R)
 73 {
 74     if(cover[rt] != -1)
 75     {
 76         qN = cover[rt];
 77         return;
 78     }
 79     pushDown(rt);
 80     int m = (l+r)>>1;
 81     if(L <= m)
 82         query(lson, L, R);
 83     if(qN == -1 && R > m)
 84         query(rson, L, R);
 85     return;
 86 }
 87 void solve(int l, int r)
 88 {
 89     if(l >= r)
 90     {
 91         return;
 92     }
 93     int m = (l+r)>>1;
 94     t1[0] = t2[0] = 0;
 95     for(int i = l; i <= r; i++)
 96     {
 97         if(rk[i] <= m)
 98         {
 99             t1[++t1[0]] = rk[i];
100         }
101         else
102         {
103             t2[++t2[0]] = rk[i];
104         }
105     }
106     queue<int> q1, q2;
107     for(int i = l; i <= r; i++)
108     {
109         if(i <= m)
110         {
111             rk[i] = t1[i-l+1];
112             if(qu[rk[i]].type == 1)
113             {
114                 q1.push(rk[i]);
115             }
116         }
117         else
118         {
119             rk[i] = t2[i-m];
120             if(qu[rk[i]].type == 2)
121                 q2.push(rk[i]);
122         }
123     }
124     cover[1] = 0;
125     ct++;
126     while(!q2.empty())
127     {
128         int xx = q2.front();
129         q2.pop();
130         int v1 = qu[xx].v;
131         while(!q1.empty() && dep[qu[q1.front()].v] <= dep[v1])
132         {
133             int  yy = q1.front();
134             q1.pop();
135             int v2 = qu[yy].v;
136             int x2 = qu[yy].x;
137             int k2 = qu[yy].k;
138             qN = -1;
139             query(1, 1, cnt, ll[v2], rr[v2]);
140             x[v2] = (x2+x[qN])%M-(LL)(dep[v2]-dep[qN])%M*(LL)k[qN]%M;
141             if(x[v2] < 0) x[v2] += M;
142             k[v2] = (k2+k[qN])%M;
143             update(1, 1, cnt, ll[v2], rr[v2], v2);
144         }
145         qN = -1;
146         query(1, 1, cnt, ll[v1], rr[v1]);
147         ans[xx] = ((ans[xx]+x[qN])%M-(LL)(dep[v1]-dep[qN])%M*(LL)k[qN]%M+M)%M;
148     }
149     solve(l, m);
150     solve(m+1, r);
151 }
152 int main()
153 {
154 
155     int n, q;
156     scanf("%d", &n);
157     for(int i = 1; i <= n-1; i++)
158     {
159         int u = i+1, v;
160         scanf("%d", &v);
161         g[v].pb(u);
162     }
163     dep[1] = 1;
164     dfs(1);
165     scanf("%d", &q);
166     for(int i = 1; i <= q; i++)
167     {
168         rk[i] = i;
169         scanf("%d", &qu[i].type);
170         if(qu[i].type == 1)
171         {
172             scanf("%d%d%d", &qu[i].v, &qu[i].x, &qu[i].k);
173         }
174         else scanf("%d", &qu[i].v);
175     }
176     sort(rk+1, rk+q+1, cmp);
177 //    for(int i = 1; i <= q; i++)
178 //        cout<<qu[rk[i]].v<<endl;
179     solve(1, q);
180     for(int i = 1; i <= q; i++)
181     {
182         if(qu[i].type == 2)
183         {
184             printf("%d\n", ans[i]);
185         }
186     }
187     return 0;
188 }
View Code

 

posted on 2014-09-01 00:59  rootial  阅读(587)  评论(0编辑  收藏  举报

导航