八十中集训解题报告
(持续更新中…………)
去八十中被屠成渣了,被屠得毫无还手之力,被屠得丧心病狂,停都停不下来,于是只好来写写题解了。
题目在openjudge上都有,自己去翻。
Day 1:
第一题:
这道概率题囧翻了…………卡精度………………
我们考虑我们枚举其中一个被解决的时候另外一个还剩多少体力,那么两个人收到伤害的次数都是固定的,假设第一个人被打了n次,第二个人被打了m次,那么就相当于将这m次不断插入到这n次之间的空去(不能插到第n次之后的空里面去),那么这就是个简单的组合计数了。
关于精度问题…………这个有点囧…………我们考虑第二个人的第m次可以由前一次推出来,然后将一些可能会导致精度下降的运算放到后面去做…………就差不多了吧…………
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<cmath>
5
6 using namespace std;
7
8 const int maxn=20010;
9 const double eps=1e-8;
10
11 int n;
12
13 double p;
14
15 long double solve(int n,long double p)
16 {
17 long double ans=n,tmp=1;
18 for (int a=1;a<=n;a++)
19 {
20 tmp*=p*(n+a)/a*(1-p);
21 ans=ans*p+tmp*(n-a);
22 }
23 return ans*p;
24 }
25
26 int main()
27 {
28 int t=0;
29 while (~scanf("%d%lf",&n,&p))
30 {
31 t++;
32 printf("Case %d: %.2lf\n",t,(double)(solve(n,p)+solve(n,1-p)));
33 }
34
35 return 0;
36 }
第二题:
我一开始拿hash裸的…………其他人拿kmp裸的好像都比我裸的少了10分…………囧…………
我们考虑这个问题的本质吧,如果我们倒着做就相当于询问前缀能否拆为若干子串使得每一个都是选出的后缀的一个前缀。这样感觉好囧的样子,于是我们换个方向。假设我们从前往后做来构造这个后缀,那么我们处理到某个位置时相当于是构造出了这个目标后缀的一个前缀,我们考虑如何构造这个前缀。我们先对整个串做一次KMP,然后从前往后走。假设我们这时走到了一个位置p,我们已经得到了一个前缀s,然后考虑我们构造新串。如果我们发现当前串无法与已构造出的前缀进行任何一位的匹配,那么我们这个时候如果想要得到一个合法的串就只有把以后造出的前缀与当前这个串进行拼接形成新的前缀,否则如果能够匹配的话则代表当前串能够与原前缀进行匹配,则说明当前前缀合法。我们考虑我们如何检验这两个串是否能够匹配,这个可以用KMP做。考虑到我们要修改这个前缀,那么解决的办法就是每次对这个前缀暴力KMP,因为考虑到每个字符最多只被重做一次,所以依然是O(n)的。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4
5 using namespace std;
6
7 const int maxn=1000010;
8
9 int n,fail[maxn];
10
11 char s[maxn];
12
13 int main()
14 {
15 int t;
16 scanf("%d",&t);
17 for (int p=1;p<=t;p++)
18 {
19 scanf("%s",s);
20 n=strlen(s);
21 fail[0]=0;
22 int b=0,last=1,lastl=1,nowp=0;
23 for (int a=1;a<n;a++)
24 {
25 while (b && s[nowp+b]!=s[a])
26 b=fail[b-1];
27 if (s[nowp+b]==s[a]) b++;
28 if (!b)
29 {
30 nowp=last-lastl;
31 int nowl=a-nowp+1;
32 for (int c=lastl;c<nowl;c++)
33 {
34 int d=fail[c-1];
35 while (d && s[nowp+d]!=s[p+c])
36 d=fail[d-1];
37 if (s[nowp+d]==s[nowp+c]) d++;
38 fail[c]=d;
39 }
40 lastl=nowl;
41 last=a+1;
42 b=fail[lastl-1];
43 }
44 else
45 {
46 if (b==lastl)
47 {
48 last=a+1;
49 b=fail[b-1];
50 }
51 }
52 }
53 printf("Case %d: %d\n",p,n-last+lastl);
54 }
55
56 return 0;
57 }
第三题:
神题…………考试的时候想多了所以打的表…………
那一大堆结论的证明自己去看当天的题解吧,我说说一种做法。
考虑一个质数的循环节既然是小于2n+2的,那么我们对每一个质因数的循环节暴力找出有多长即可了(因为最大的质因数也不大),然后考虑将各个质因数组合起来的方式,利用题解中那个公式就行了,总之一切皆为暴力。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4
5 using namespace std;
6
7 #ifdef unix
8 #define LL "%lld"
9 #else
10 #define LL "%I64d"
11 #endif
12
13 const int maxn=1000000;
14
15 int n;
16
17 long long gcd(long long a,long long b)
18 {
19 if (a % b==0) return b;
20 else return gcd(b,a%b);
21 }
22
23 int find(int mo)
24 {
25 int a=1,b=1,c=0,d=2;
26 while(true)
27 {
28 d++;
29 c=a+b;
30 if (c>=mo) c-=mo;
31 b=a;
32 a=c;
33 if (a==1 && b==0) return d-1;
34 }
35 return -1;
36 }
37
38 int main()
39 {
40 int t;
41 scanf("%d",&t);
42 for (int p=1;p<=t;p++)
43 {
44 scanf("%d",&n);
45 if (n==1)
46 {
47 printf("1\n");
48 continue;
49 }
50 long long ans=1;
51 for (int a=2;a*a<=n;a++)
52 if (n % a==0)
53 {
54 int nowans=find(a);
55 n/=a;
56 while (n%a==0)
57 {
58 n/=a;
59 nowans*=a;
60 }
61 ans=ans/gcd(ans,nowans)*nowans;
62 }
63 if (n!=1)
64 {
65 int nowans=find(n);
66 ans=ans/gcd(ans,nowans)*nowans;
67 }
68 printf(LL "\n",ans);
69 }
70 }
Day 2:
第一题:
这题太囧了啊………………我树分治T到不行…………暴力的反而跑得硕快无比…………
我最后是这样做的,因为对于一棵子树只用考虑过根的路径,那么我们对每棵子树建一棵splay,维护它这棵子树中的每个节点到它的距离,进行合并的时候启发式合并即可。当然,你构造一根链我就GG了………………………然后你每次而二分个答案,然后找出比这个大的路径有多少就可以判定答案了………………判定可以用维护的那个splay乱搞就行了…………囧………………
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<algorithm>
5
6 using namespace std;
7
8 const int maxn=50010;
9 const int maxm=300010;
10 const int maxp=5000010;
11 const int INF=0x3f3f3f3f;
12
13 int en,nowlimit,n,m,ans[maxm],now,cnt,z[maxn],nowl,l;
14
15 bool flag;
16
17 struct node
18 {
19 node *l,*r,*f;
20 int size,cnt,v;
21 node()
22 {
23 l=r=f=NULL;
24 size=cnt=v=0;
25 }
26 }pp[maxp],*ppp=pp;
27
28 node *newnode()
29 {
30 return ppp++;
31 }
32
33 struct splay_tree
34 {
35 node *root;
36 void clear()
37 {
38 root=NULL;
39 }
40 void update(node *p)
41 {
42 p->size=p->cnt;
43 if (p->l) p->size+=p->l->size;
44 if (p->r) p->size+=p->r->size;
45 }
46 void rot_l(node *now)
47 {
48 node *x=now->r;
49 now->r=x->l;
50 x->l=now;
51 if (now->f==NULL) root=x;
52 else
53 {
54 if (now->f->l==now) now->f->l=x;
55 else now->f->r=x;
56 }
57 x->f=now->f;
58 now->f=x;
59 if (now->r) now->r->f=now;
60 update(now);
61 update(x);
62 }
63 void rot_r(node *now)
64 {
65 node *x=now->l;
66 now->l=x->r;
67 x->r=now;
68 if (now->f==NULL) root=x;
69 else
70 {
71 if (now->f->l==now) now->f->l=x;
72 else now->f->r=x;
73 }
74 x->f=now->f;
75 now->f=x;
76 if (now->l) now->l->f=now;
77 update(now);
78 update(x);
79 }
80 void splay(node *now,node *goal)
81 {
82 while (now->f!=goal)
83 {
84 node *f=now->f;
85 node *ff=f->f;
86 if (ff==goal)
87 {
88 if (f->l==now) rot_r(f);
89 else rot_l(f);
90 }
91 else
92 {
93 if (ff->l==f && f->l==now)
94 {
95 rot_r(ff);
96 rot_r(f);
97 }
98 if (ff->l==f && f->r==now)
99 {
100 rot_l(f);
101 rot_r(ff);
102 }
103 if (ff->r==f && f->l==now)
104 {
105 rot_r(f);
106 rot_l(ff);
107 }
108 if (ff->r==f && f->r==now)
109 {
110 rot_l(ff);
111 rot_l(f);
112 }
113 }
114 }
115 }
116 void insert(int v)
117 {
118 if (root==NULL)
119 {
120 root=newnode();
121 root->cnt=1;
122 root->size=1;
123 root->v=v;
124 }
125 else
126 {
127 node *p=root,*lastp;
128 while (p)
129 {
130 if (p->v==v)
131 {
132 p->cnt++;
133 splay(p,NULL);
134 return;
135 }
136 lastp=p;
137 if (p->v<v) p=p->r;
138 else p=p->l;
139 }
140 p=newnode();
141 p->cnt=p->size=1;
142 p->v=v;
143 p->f=lastp;
144 if (v<lastp->v) lastp->l=p;
145 else lastp->r=p;
146 splay(p,NULL);
147 }
148 }
149 int count(int v)
150 {
151 node *p=root;
152 int ans=0;
153 while (true)
154 {
155 if (v>p->v)
156 {
157 if (p->r) p=p->r;
158 else
159 {
160 splay(p,NULL);
161 return ans;
162 }
163 }
164 else
165 {
166 ans+=p->cnt;
167 if (p->r) ans+=p->r->size;
168 if (p->v==v)
169 {
170 splay(p,NULL);
171 return ans;
172 }
173 else
174 {
175 if (p->l) p=p->l;
176 else
177 {
178 splay(p,NULL);
179 return ans;
180 }
181 }
182 }
183 }
184 return ans;
185 }
186 void dfs(node *p)
187 {
188 if (!p) return;
189 dfs(p->l);
190 for (int a=1;a<=p->cnt;a++)
191 {
192 l++;
193 z[l]=p->v;
194 }
195 dfs(p->r);
196 }
197 void dfs2(node *p)
198 {
199 if (!p) return;
200 for (int a=1;a<=p->cnt;a++)
201 {
202 nowl++;
203 ans[nowl]=p->v;
204 }
205 dfs2(p->l);
206 dfs2(p->r);
207 }
208 void outit(int v)
209 {
210 node *p=root;
211 while (true)
212 {
213 if (v>p->v)
214 {
215 if (p->r) p=p->r;
216 else
217 {
218 splay(p,NULL);
219 return;
220 }
221 }
222 else
223 {
224 for (int a=1;a<=p->cnt;a++)
225 {
226 nowl++;
227 ans[nowl]=p->v;
228 }
229 if (p->r) dfs2(p->r);
230 if (p->v==v)
231 {
232 splay(p,NULL);
233 return;
234 }
235 else
236 {
237 if (p->l) p=p->l;
238 else
239 {
240 splay(p,NULL);
241 return;
242 }
243 }
244 }
245 }
246 }
247 }tree[maxn];
248
249 struct edge
250 {
251 int e,d;
252 edge *next;
253 }*v[maxn],ed[maxn<<1];
254
255 void add_edge(int s,int e,int d)
256 {
257 en++;
258 ed[en].next=v[s];v[s]=ed+en;v[s]->e=e;v[s]->d=d;
259 }
260
261 void merge(splay_tree &t1,splay_tree &t2,int limit)
262 {
263 if (t1.root->size<t2.root->size) swap(t1,t2);
264 l=0;
265 t2.dfs(t2.root);
266 for (int a=1;a<=l;a++)
267 cnt+=t1.count(nowlimit+2*limit-z[a]);
268 for (int a=1;a<=l;a++)
269 t1.insert(z[a]);
270 }
271
272 void getans(splay_tree &t1,splay_tree &t2,int limit)
273 {
274 if (t1.root->size<t2.root->size) swap(t1,t2);
275 l=0;
276 t2.dfs(t2.root);
277 for (int a=1;a<=l;a++)
278 {
279 int lastl=nowl;
280 t1.outit(nowlimit+2*limit-z[a]);
281 for (int b=lastl+1;b<=nowl;b++)
282 ans[b]+=z[a]-2*limit;
283 }
284 for (int a=1;a<=l;a++)
285 t1.insert(z[a]);
286 }
287
288 void dfs(int now,int pre,int dist)
289 {
290 tree[now].insert(dist);
291 for (edge *e=v[now];e;e=e->next)
292 if (e->e!=pre)
293 {
294 dfs(e->e,now,dist+e->d);
295 if (flag) getans(tree[now],tree[e->e],dist);
296 else merge(tree[now],tree[e->e],dist);
297 }
298 }
299
300 void check(int limit)
301 {
302 cnt=0;
303 nowlimit=limit;
304 for (int a=1;a<=n;a++)
305 tree[a].clear();
306 dfs(1,0,0);
307 }
308
309 int main()
310 {
311
312 scanf("%d%d",&n,&m);
313 for (int a=1;a<n;a++)
314 {
315 int s,e,d;
316 scanf("%d%d%d",&s,&e,&d);
317 add_edge(s,e,d);
318 add_edge(e,s,d);
319 }
320 int l=0,r=INF;
321 while (l+1!=r)
322 {
323 int mid=(l+r)>>1;
324 check(mid);
325 if (cnt>m) l=mid;
326 else r=mid;
327 }
328 flag=true;
329 check(r);
330 sort(ans+1,ans+m+1);
331 for (int a=m;a>=1;a--)
332 printf("%d\n",ans[a]);
333
334 return 0;
335 }
第二题:
DP。
考虑对人员安排顺序固定了的一个序列,我们可以用f[i][j][k]表示前i个人放在了前j个位置且最后d个位置的的状态为k的方案数,这个dp还是比较水的。
然后我们考虑如何定序,假设我们有一个已有序列,那么我们需要放入下一个人,考虑由于每个人与他的朋友的距离都不能超过d,那么我们下一个放入的人一定是与已在序列中是朋友数最多的人(感觉很显然),如果有多个这样的人,那么我们需要放入的就是这些人中与不在序列中的人朋友最少的(还是很显然),如果有多个这样的人,那么他们是同构的,那么这个时候就把他们都加入序列,因为他们是同构的,也就是说当我们确定了第一个人是谁之后,整个序列也随之确定,那么问题迎刃而解。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<algorithm>
5
6 using namespace std;
7
8 const int maxn=52;
9 const int maxm=110;
10 const int maxd=8;
11 const int mo=1000000007;
12 const int INF=0x3f3f3f3f;
13
14 #define inc(a,b) {(a)+=(b);if ((a)>=mo) (a)-=mo;}
15
16 int in[maxn],to[maxn],f[maxn][maxm][1<<maxd],q[maxn],n,m,d;
17
18 char s[maxn][maxn];
19
20 bool use[maxn];
21
22 bool check()
23 {
24 for (int a=1;a<=n;a++)
25 for (int b=a-1;b>=1;b--)
26 if (!s[q[a]][q[b]])
27 {
28 for (int c=b-1;c>=1;c--)
29 if (s[q[a]][q[c]]) return false;
30 break;
31 }
32 for (int a=1;a<=n;a++)
33 for (int b=a-d-1;b>=1;b--)
34 if (s[q[a]][q[b]]) return false;
35 return true;
36 }
37
38
39 int dp()
40 {
41 if (!check()) return 0;
42 int ans=0;
43 memset(f,0,sizeof(f));
44 for (int a=1;a<=m;a++)
45 f[1][a][1]=1;
46 for (int a=1;a<n;a++)
47 for (int b=a;b<=m;b++)
48 for (int c=0;c<(1<<d);c++)
49 if (f[a][b][c])
50 {
51 if (s[q[a]][q[a+1]])
52 {
53 int nowp=a-1;
54 while (nowp && s[q[nowp]][q[a+1]])
55 nowp--;
56 nowp++;
57 int nows=c;
58 int cnt=0,p=-1;
59 while (nows)
60 {
61 if (nows&1) cnt++;
62 nows>>=1;
63 p++;
64 if (nowp+cnt==a+1)
65 {
66 int pp=0,nows=c,cntt=0,uf=-INF;
67 while (nows)
68 {
69 if (nows&1) cntt++;
70 if (!s[q[a-cntt+1]][q[a+1]])
71 {
72 uf=b-pp;
73 break;
74 }
75 pp++;
76 nows>>=1;
77 }
78 for (int e=max(uf+d+1,b+1);e<=b-p+d;e++)
79 inc(f[a+1][e][(1|((c&((1<<(d+b-e))-1))<<(e-b)))&((1<<d)-1)],f[a][b][c]);
80 break;
81 }
82 }
83 }
84 else
85 {
86 for (int e=b+d+1;e<=m;e++)
87 inc(f[a+1][e][1],f[a][b][c]);
88 }
89 }
90 for (int a=n;a<=m;a++)
91 for (int b=0;b<(1<<d);b++)
92 if (f[n][a][b]) inc(ans,f[n][a][b]);
93 return ans;
94 }
95
96 int dfs(int nowp)
97 {
98 if (nowp>n) return dp();
99 int nowans=0,nowinmax=-1,nowoutmin=INF;
100 for (int a=1;a<=n;a++)
101 if (!use[a])
102 {
103 if (in[a]>nowinmax)
104 {
105 nowinmax=in[a];
106 nowoutmin=to[a]-in[a];
107 }
108 if (in[a]==nowinmax && nowoutmin>to[a]-in[a]) nowoutmin=to[a]-in[a];
109 }
110 int cnt=0;
111 for (int a=1;a<=n;a++)
112 if (!use[a] && in[a]==nowinmax && nowoutmin==to[a]-in[a])
113 {
114 cnt++;
115 q[nowp+cnt-1]=a;
116 use[a]=true;
117 }
118 for (int a=nowp;a<=nowp+cnt-1;a++)
119 for (int b=1;b<=n;b++)
120 in[b]+=s[q[a]][b];
121 nowans=dfs(nowp+cnt);
122 for (int a=1;a<=cnt;a++)
123 nowans=(long long)nowans*a%mo;
124 for (int a=nowp;a<=nowp+cnt-1;a++)
125 for (int b=1;b<=n;b++)
126 in[b]-=s[q[a]][b];
127 for (int a=nowp;a<=nowp+cnt-1;a++)
128 use[q[a]]=false;
129 return nowans;
130 }
131
132 int main()
133 {
134
135 scanf("%d%d%d",&m,&n,&d);
136 for (int a=1;a<=n;a++)
137 scanf("%s",s[a]+1);
138 for (int a=1;a<=n;a++)
139 for (int b=1;b<=n;b++)
140 if (s[a][b]=='Y') s[a][b]=1,to[a]++;
141 else s[a][b]=0;
142 int ans=0;
143 for (int a=1;a<=n;a++)
144 {
145 q[1]=a;
146 for (int b=1;b<=n;b++)
147 in[b]+=s[a][b];
148 use[a]=true;
149 inc(ans,dfs(2));
150 use[a]=false;
151 for (int b=1;b<=n;b++)
152 in[b]-=s[a][b];
153 }
154 printf("%d\n",ans);
155
156 return 0;
157 }
第三题:
这题jzp讲了一种logn的做法,但是没听懂…………囧…………
这道题其实有sqrt(n)的做法,因为考虑我们可以暴力出前sqrt(n)个数的值,那么考虑对于下sqrt(n)个数的值的一个分子增量范围是可以算出来的,并且会有一部分的增量比另外一部分多1,那么我们可以根据它们原来的关于模B的值排序,每次就可以二分出变化位置,从而推出下sqrt(n)个数的和。然后就可以做sqrt(n)次之后算出所有的答案。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<cmath>
5 #include<algorithm>
6
7 using namespace std;
8
9 #ifdef unix
10 #define LL "%lld"
11 #else
12 #define LL "%I64d"
13 #endif
14
15 const int maxn=100000;
16 const long long mo=1000000007;
17
18 long long va,vb,vc,l,r;
19
20 struct state
21 {
22 long long mod,x,y,z;
23 bool operator<(const state &a)const
24 {
25 return mod<a.mod;
26 }
27 }s[maxn],sum[maxn],bsum[maxn];
28
29 int find(int size,long long delta_mod)
30 {
31 if (delta_mod<s[1].mod) return 1;
32 if (delta_mod>s[size].mod) return size+1;
33 int l=0,r=size;
34 while (l+1!=r)
35 {
36 int m=(l+r)>>1;
37 if (s[m].mod>=delta_mod) r=m;
38 else l=m;
39 }
40 return r;
41 }
42
43 long long getans(int size,long long nowp)
44 {
45 long long delta_mod=nowp*va%vb;
46 int p=find(size,vb-delta_mod);
47 long long deltav=nowp*va/vb%mo;
48 long long ans=0;
49 long long delta=nowp;
50 p--;
51 if (p) ans=(ans+sum[p].z+sum[p].x*deltav%mo+delta*sum[p].y%mo+delta*deltav%mo*p%mo)%mo;
52 p++;
53 deltav++;
54 if (p<=size) ans=(ans+bsum[p].z+bsum[p].x*deltav%mo+delta*bsum[p].y%mo+delta*deltav%mo*(size-p+1)%mo)%mo;
55 return ans;
56 }
57
58 long long solve(long long p)
59 {
60 if (p==0) return 0;
61 int size=(int)sqrt(p);
62 for (int a=1;a<=size;a++)
63 {
64 s[a].mod=(va*a+vc)%vb;
65 s[a].x=a;
66 s[a].y=(va*a+vc)/vb%mo;
67 s[a].z=s[a].x*s[a].y%mo;
68 }
69 sort(s+1,s+size+1);
70 sum[0].x=sum[0].y=sum[0].z=0;
71 for (int a=1;a<=size;a++)
72 sum[a].x=(sum[a-1].x+s[a].x)%mo,sum[a].y=(sum[a-1].y+s[a].y)%mo,sum[a].z=(sum[a-1].z+s[a].z)%mo;
73 bsum[size+1].x=bsum[size+1].y=bsum[size+1].z=0;
74 for (int a=size;a>=1;a--)
75 bsum[a].x=(bsum[a+1].x+s[a].x)%mo,bsum[a].y=(bsum[a+1].y+s[a].y)%mo,bsum[a].z=(bsum[a+1].z+s[a].z)%mo;
76 long long nowp=1,ans=0;
77 while (nowp+size-1<=p)
78 {
79 ans=(ans+getans(size,nowp-1))%mo;
80 nowp+=size;
81 }
82 for (long long a=nowp;a<=p;a++)
83 ans=(ans+(va*a+vc)/vb%mo*a%mo)%mo;
84 return ans;
85 }
86
87 int main()
88 {
89 scanf(LL LL LL LL LL,&va,&vb,&vc,&l,&r);
90 printf(LL "\n",(solve(r)-solve(l-1)+mo)%mo);
91
92 return 0;
93 }
Day 3:
第一题:
这道题最大的很好做,做最小的时候每次二分这个政党最少能够获得多少席位,然后用一个背包DP检验即可。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<algorithm>
5
6 using namespace std;
7
8 const int maxn=110;
9
10 int z[maxn],s[maxn],v,n,m,maxv[maxn],sum,x[maxn],minv[maxn],f[2][maxn<<1];
11
12 struct rec
13 {
14 int v,p;
15 bool operator<(const rec &a)const
16 {
17 return v<a.v;
18 }
19 }y[maxn];
20
21 int calc(int p)
22 {
23 for (int a=1;a<=n;a++)
24 s[a]=0;
25 for (int a=1;a<=m;a++)
26 {
27 pair<int,int> nowv=make_pair(0,1);
28 int nowp=-1;
29 for (int b=1;b<=n;b++)
30 if (z[b]*20>=v && nowv.first*(s[b]+1)<nowv.second*z[b])
31 {
32 nowv=make_pair(z[b],s[b]+1);
33 nowp=b;
34 }
35 if (nowp==-1) break;
36 s[nowp]++;
37 }
38 return s[p];
39 }
40
41 bool check(int p,int goal)
42 {
43 int last=0,src=1,limit=m-goal;
44 for (int a=1;a<=limit;a++)
45 f[src][a]=v+1;
46 f[src][0]=0;
47 for (int a=1;a<=n;a++)
48 if (a!=p)
49 {
50 src^=1;
51 last^=1;
52 for (int b=0;b<=limit;b++)
53 f[src][b]=f[last][b];
54 for (int b=1;b<=limit;b++)
55 {
56 int need=z[p]*b/(goal+1)+1;
57 if (z[p]*b%(goal+1)==0) need--;
58 if (need*20<v)
59 {
60 need=v/20;
61 if (need*20<v) need++;
62 }
63 need-=z[a];
64 if (need<0) need=0;
65 for (int c=b;c<=limit;c++)
66 f[src][c]=min(f[src][c],f[last][c-b]+need);
67 }
68 }
69 return f[src][limit]<=v-sum;
70 }
71
72 int main()
73 {
74 scanf("%d%d%d",&v,&n,&m);
75 for (int a=1;a<=n;a++)
76 scanf("%d",&z[a]),sum+=z[a];
77 for (int a=1;a<=n;a++)
78 {
79 z[a]+=v-sum;
80 maxv[a]=calc(a);
81 z[a]-=v-sum;
82 }
83 for (int a=1;a<=n;a++)
84 {
85 printf("%d",maxv[a]);
86 if (a==n) printf("\n");
87 else printf(" ");
88 }
89 for (int a=1;a<=n;a++)
90 if (z[a]*20>=v || n==1)
91 {
92 int l=-1,r=m;
93 while (l+1<r)
94 {
95 int m=(l+r)>>1;
96 if (check(a,m)) r=m;
97 else l=m;
98 }
99 minv[a]=r;
100 }
101 for (int a=1;a<=n;a++)
102 {
103 printf("%d",minv[a]);
104 if (a==n) printf("\n");
105 else printf(" ");
106 }
107
108 return 0;
109 }
第二题:
这题是神一般的DP,用f[i][j][k]表示从i点走到j点走了k步没有剩余字母的方案数,用g[i][j][k]表示从i点走到j点走了k步没有剩余字母且字母是在最后一步被消耗光的方案数,用h[i][j][k]表示从i点走到j点走了k步并且有字母剩余的方案数,然后三个互相推,就可以转移了。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<vector>
5 #include<map>
6
7 using namespace std;
8
9 const int maxn=52;
10 const int maxm=maxn*maxn;
11 const int mo=10007;
12
13 #define inc(a,b) {a+=b;if (a>=mo) a-=mo;}
14
15 int en,n,m,k,g[maxn][maxn][maxn],f[maxn][maxn][maxn],h[maxn][maxn][maxn];
16
17 int s1[maxm],e1[maxm],t1[maxm],cnt1;
18
19 int s2[maxm],e2[maxm],t2[maxm],cnt2;
20
21 struct edge
22 {
23 int e;
24 char v;
25 edge *next;
26 }*v[maxn],ed[maxn*maxn],*v2[maxn];
27
28 void add_edge(int s,int e,char c)
29 {
30 en++;
31 ed[en].next=v[s];v[s]=ed+en;v[s]->e=e;v[s]->v=c;
32 cnt1++;
33 s1[cnt1]=s;e1[cnt1]=e;t1[cnt1]=c;
34 if (c!='!') t1[cnt1]-='A'-1;
35 else t1[cnt1]=0;
36 }
37
38 void add_edge2(int s,int e,char c)
39 {
40 en++;
41 ed[en].next=v2[s];v2[s]=ed+en;v2[s]->e=e;v2[s]->v=c;
42 cnt2++;
43 s2[cnt2]=s;e2[cnt2]=e;t2[cnt2]=c-'a'+1;
44 }
45
46 int main()
47 {
48 scanf("%d%d%d",&n,&m,&k);
49 for (int a=1;a<=m;a++)
50 {
51 int s,e;
52 scanf("%d%d",&s,&e);
53 char c='!';
54 bool find=false;
55 while (~scanf("%c",&c))
56 {
57 if (c=='\n' || c=='\r') break;
58 if ((c>='A' && c<='Z') || (c>='a' && c<='z'))
59 {
60 find=true;
61 if (c>='A' && c<='Z') add_edge(s,e,c);
62 else add_edge2(s,e,c);
63 break;
64 }
65 }
66 if (!find) c='!',add_edge(s,e,c);
67 }
68 for (int a=1;a<=n;a++)
69 f[a][a][0]=1,g[a][a][0]=1;
70 for (int a=1;a<=cnt1;a++)
71 for (int b=1;b<=cnt2;b++)
72 if (e1[a]==s2[b] && t1[a]==t2[b]) g[s1[a]][e2[b]][2]++;
73 for (int a=1;a<=k;a++)
74 {
75 for (int b=1;b<=n;b++)
76 for (int c=1;c<=n;c++)
77 {
78 int &x=f[b][c][a];
79 for (edge *e=v[b];e;e=e->next)
80 if (e->v=='!')
81 {
82 x+=f[e->e][c][a-1];
83 if (x>=mo) x-=mo;
84 }
85 for (int d=1;d<=n;d++)
86 for (int e=2;e<=a;e++)
87 x=(x+g[b][d][e]*f[d][c][a-e])%mo;
88 }
89 if (a+2<=k)
90 {
91 for (int b=1;b<=cnt1;b++)
92 if (t1[b])
93 for (int c=1;c<=cnt2;c++)
94 if (t1[b]==t2[c])
95 {
96 int &x=g[s1[b]][e2[c]][a+2];
97 x+=f[e1[b]][s2[c]][a];
98 if (x>=mo) x-=mo;
99 }
100 }
101 }
102 for (int a=1;a<=cnt1;a++)
103 if (t1[a]) h[s1[a]][e1[a]][1]++;
104 for (int a=2;a<=k;a++)
105 for (int b=1;b<=n;b++)
106 for (int c=1;c<=n;c++)
107 {
108 int &x=h[b][c][a];
109 for (edge *e=v[b];e;e=e->next)
110 if (e->v!='!')
111 {
112 x+=h[e->e][c][a-1]+f[e->e][c][a-1];
113 if (x>=mo) x-=mo;
114 if (x>=mo) x-=mo;
115 }
116 else
117 {
118 x+=h[e->e][c][a-1];
119 if (x>=mo) x-=mo;
120 }
121 for (int d=1;d<=n;d++)
122 for (int e=2;e<a;e++)
123 x=(x+g[b][d][e]*h[d][c][a-e])%mo;
124 }
125 int ans=0;
126 for (int a=0;a<=k;a++)
127 ans=(ans+f[1][n][a]+h[1][n][a])%mo;
128 printf("%d\n",ans);
129
130 return 0;
131 }
第三题:
可以很容易发现题目就是问一条直线Y=kX+b中的b最大值,那么考虑这个问题实际上就是在一个上凸壳中询问一条与y轴截距最大的直线,那么每次二分答案就可以解决了。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4
5 using namespace std;
6
7 #ifdef unix
8 #define LL "%lld"
9 #else
10 #define LL "%I64d"
11 #endif
12
13 const int maxn=100010;
14
15 int n,m,size;
16
17 long long x[maxn],y[maxn],sum[maxn],f[maxn];
18
19 long long cross(long long x1,long long y1,long long x2,long long y2)
20 {
21 return x1*y2-x2*y1;
22 }
23
24 void get_hull()
25 {
26 x[1]=0;y[1]=sum[1];
27 x[2]=sum[1];y[2]=sum[2];
28 size=2;
29 for (int a=3;a<=n;a++)
30 {
31 long long xx=sum[a-1],yy=sum[a];
32 while (size && cross(xx-x[size],yy-y[size],xx-x[size-1],yy-y[size-1])<=0)
33 size--;
34 size++;
35 x[size]=xx;
36 y[size]=yy;
37 }
38 }
39
40 int main()
41 {
42
43 scanf("%d%d",&n,&m);
44 for (int a=1;a<=n;a++)
45 scanf(LL,&sum[a]),sum[a]+=sum[a-1];
46 for (int a=1;a<=m;a++)
47 scanf(LL,&f[a]);
48 get_hull();
49 long long ans=0;
50 for (int a=2;a<=m;a++)
51 {
52 if (size==1) ans+=f[a-1]*y[1];
53 else
54 {
55 if (cross(f[a-1],f[a],x[2]-x[1],y[2]-y[1])<=0) ans+=f[a-1]*y[1]-f[a]*x[1];
56 else
57 {
58 if (cross(x[size]-x[size-1],y[size]-y[size-1],f[a-1],f[a])<=0) ans+=f[a-1]*y[size]-f[a]*x[size];
59 else
60 {
61 int l=2,r=size+1;
62 while (l+1!=r)
63 {
64 int m=(l+r)>>1;
65 if (cross(x[m]-x[m-1],y[m]-y[m-1],f[a-1],f[a])<=0) l=m;
66 else r=m;
67 }
68 ans+=f[a-1]*y[l]-f[a]*x[l];
69 }
70 }
71 }
72 }
73 ans+=sum[n]*f[m];
74 printf(LL "\n",ans);
75
76 return 0;
77 }
Day 4:
第一题:
这题太囧了………………其实就是要求动态维护重心………………
考虑每次合并两颗子树的时候新的重心一定在两颗树的原来之间的路径上,那么我们可以用LCT将这两个点到他们的LCA的路径搞出来,然后我们中途动态维护一些值就可以做了,这种做法可以去看fhq的博客。
我用的是一种囧到不能再囧的方法…………我们先把所有询问离线,然后在预处理时只考虑加边操作,每次进行启发式合并,将小树的dfs序并到大树的dfs序的后面去,这样可以在O(nlogn)的时间内将所有时刻对应的不同的dfs序给搞出来。然后再来做询问,考虑每次需要维护重心,而重心只会在加边的时候改变。这个时候我们暴力修改小树的倍增父亲的值,然后找出原来两个重心的LCA,然后就相当于是在这两个链上找到一个重心。考虑我们从大树的重心向上移,每移一次我们需要知道所有点到这两个点的距离总和,相当于只要我们知道这棵子树的大小就可以算出这个差量了。由于我们之前维护了一个任意时刻的dfs序,所以我们直接使用树状数组维护每一个dfs序,这样就可以询问一颗子树的当前大小,从而解决这个增量的问题。然后可以发现这个是有一定的单调性的,所以利用那个维护的倍增父亲的值就可以顺利解决了。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<vector>
5
6 using namespace std;
7
8 const int maxn=100010;
9 const int maxq=200010;
10
11 #define lb(a) ((a)&(-(a)))
12
13 int n,m,en,depth[maxn],q[maxq][3],size[maxn],root[maxn],f[maxn][20],mid[maxn],id[maxn],idx,p[maxn],belong[maxq+maxn],tmp,ans;
14
15 char s[10];
16
17 struct rec
18 {
19 int id,pos;
20 rec(){}
21 rec(int a,int b)
22 {
23 id=a;pos=b;
24 }
25 };
26
27 vector<rec> into[maxn],outto[maxn];
28
29 vector<int> tree[maxq];
30
31 struct edge
32 {
33 int e;
34 edge *next;
35 }*v[maxn],ed[maxn<<1];
36
37 void add_edge(int s,int e)
38 {
39 en++;
40 ed[en].next=v[s];v[s]=ed+en;v[s]->e=e;
41 }
42
43 void add(int id,int p)
44 {
45 while (p<(int)tree[id].size())
46 {
47 tree[id][p]++;
48 p+=lb(p);
49 }
50 }
51
52 int query(int id,int p)
53 {
54 int ans=0;
55 while (p)
56 {
57 ans+=tree[id][p];
58 p-=lb(p);
59 }
60 return ans;
61 }
62
63 void build(int now,int fa,int id)
64 {
65 if (fa==0) tree[id].push_back(0);
66 tree[id].push_back(0);
67 into[now].push_back(rec(id,tree[id].size()-1));
68 for (edge *e=v[now];e;e=e->next)
69 if (e->e!=fa) build(e->e,now,id);
70 tree[id].push_back(0);
71 outto[now].push_back(rec(id,tree[id].size()-1));
72 }
73
74 void change(int now,int fa,int rt,bool dep)
75 {
76 root[now]=rt;
77 if (dep)
78 {
79 depth[now]=depth[fa]+1;
80 memset(f[now],0,sizeof(f[now]));
81 f[now][0]=fa;
82 int x=0,nowp=fa;
83 while (f[nowp][x])
84 {
85 f[now][x+1]=f[nowp][x];
86 nowp=f[nowp][x];
87 x++;
88 }
89 }
90 for (edge *e=v[now];e;e=e->next)
91 if (e->e!=fa) change(e->e,now,rt,dep);
92 }
93
94 void prepare()
95 {
96 for (int a=1;a<=n;a++)
97 {
98 size[a]=depth[a]=1;
99 root[a]=id[a]=a;
100 }
101 idx=n;
102 for (int a=1;a<=m;a++)
103 if (q[a][0]==1)
104 {
105 int p1=q[a][1],p2=q[a][2];
106 if (size[root[p1]]<size[root[p2]]) swap(p1,p2);
107 if (size[root[p1]]==size[root[p2]] && p1>p2) swap(p1,p2);
108 int r1=root[p1],r2=root[p2];
109 size[r1]+=size[r2];
110 build(r2,0,id[r2]);
111 add_edge(p1,p2);
112 add_edge(p2,p1);
113 change(p2,p1,r1,false);
114 idx++;
115 belong[id[r1]]=idx;
116 id[r1]=idx;
117 }
118 for (int a=1;a<=n;a++)
119 if (root[a]==a) build(a,0,id[a]);
120 for (int a=idx;a>=1;a--)
121 if (belong[a]==0) belong[a]=a;
122 else belong[a]=belong[belong[a]];
123 }
124
125 void insert(int now,int id)
126 {
127 while (into[now][p[now]].id!=id)
128 p[now]++;
129 add(id,into[now][p[now]].pos);
130 }
131
132 void insert(int now,int fa,int id)
133 {
134 insert(now,id);
135 for (edge *e=v[now];e;e=e->next)
136 if (e->e!=fa) insert(e->e,now,id);
137 }
138
139 int get_lca(int p1,int p2)
140 {
141 if (depth[p1]<depth[p2]) swap(p1,p2);
142 int delta=depth[p1]-depth[p2],x=0;
143 while (delta)
144 {
145 if (delta&1) p1=f[p1][x];
146 x++;
147 delta>>=1;
148 }
149 x=0;
150 while (p1!=p2)
151 {
152 if (!x || f[p1][x]!=f[p2][x])
153 {
154 p1=f[p1][x];
155 p2=f[p2][x];
156 x++;
157 }
158 else x--;
159 }
160 return p1;
161 }
162
163 int get_sum(int now,int id)
164 {
165 int ans=0;
166 while (outto[now][p[now]].id!=id)
167 p[now]++;
168 ans=query(id,outto[now][p[now]].pos);
169 while (into[now][p[now]].id!=id)
170 p[now]++;
171 ans-=query(id,into[now][p[now]].pos-1);
172 return ans;
173 }
174
175 int get_sum2(int now,int id)
176 {
177 int ans=0;
178 int l=-1,r=outto[now].size()-1;
179 while (l+1!=r)
180 {
181 int m=(l+r)>>1;
182 if (outto[now][m].id>=id) r=m;
183 else l=m;
184 }
185 ans=query(id,outto[now][r].pos);
186 l=-1,r=into[now].size()-1;
187 while (l+1!=r)
188 {
189 int m=(l+r)>>1;
190 if (into[now][m].id>=id) r=m;
191 else l=m;
192 }
193 ans-=query(id,into[now][r].pos-1);
194 return ans;
195 }
196
197 int get_next(int p1,int p2)
198 {
199 int delta=depth[p2]-depth[p1]-1;
200 int x=0;
201 if (delta<0) return -1;
202 while (delta)
203 {
204 if (delta&1) p2=f[p2][x];
205 x++;
206 delta>>=1;
207 }
208 return p2;
209 }
210
211 void get_change(int &nowmid,int idx,int next)
212 {
213 int p=nowmid;
214 int ep=f[nowmid][0];
215 int delta;
216 if (ep)
217 {
218 delta=size[root[nowmid]]-2*get_sum2(nowmid,belong[idx]);
219 if (delta==0 && ep<p) p=ep;
220 }
221 if (next!=-1)
222 {
223 delta=size[root[nowmid]]-2*get_sum2(next,belong[idx]);
224 if (delta==0 && next<p) p=next;
225 }
226 nowmid=p;
227 }
228
229 int dfs(int now,int fa)
230 {
231 int nowsum=1;
232 for (edge *e=v[now];e;e=e->next)
233 if (e->e!=fa) nowsum+=dfs(e->e,now);
234 tmp+=nowsum;
235 return nowsum;
236 }
237
238 void solve()
239 {
240 en=0;
241 for (int a=1;a<=n;a++)
242 {
243 size[a]=1;
244 root[a]=mid[a]=id[a]=a;
245 insert(a,belong[a]);
246 v[a]=NULL;
247 ans^=a;
248 }
249 idx=n;
250 for (int a=1;a<=m;a++)
251 if (q[a][0]==2) printf("%d\n",ans);
252 else
253 {
254 if (q[a][0]==3) printf("%d\n",mid[root[q[a][1]]]);
255 else
256 {
257 int p1=q[a][1],p2=q[a][2];
258 if (size[root[p1]]<size[root[p2]]) swap(p1,p2);
259 if (size[root[p1]]==size[root[p2]] && p1>p2) swap(p1,p2);
260 int r1=root[p1],r2=root[p2];
261 int ans1=mid[r1],ans2=mid[r2];
262 size[r1]+=size[r2];
263 add_edge(p1,p2);
264 add_edge(p2,p1);
265 change(p2,p1,r1,true);
266 int lca=get_lca(mid[r1],p1);
267 idx++;
268 id[r1]=idx;
269 insert(p2,p1,belong[idx]);
270 tmp=0;
271 dfs(p2,p1);
272 int nowmid=mid[r1];
273 while (nowmid!=lca)
274 {
275 int nowsum=get_sum(nowmid,belong[idx]);
276 if (size[r1]>(nowsum<<1)) nowmid=f[nowmid][0];
277 else break;
278 }
279 if (nowmid!=lca)
280 {
281 int next=get_next(nowmid,r1);
282 get_change(nowmid,idx,next);
283 mid[r1]=nowmid;
284 ans^=mid[r1]^ans1^ans2;
285 continue;
286 }
287 int x=0;
288 nowmid=p1;
289 while (f[nowmid][x])
290 x++;
291 while (x>=0)
292 {
293 if (f[nowmid][x])
294 {
295 int nowsum=get_sum(f[nowmid][x],belong[idx]);
296 if (size[r1]>=(nowsum<<1)) nowmid=f[nowmid][x];
297 }
298 x--;
299 }
300 tmp=2*get_sum(nowmid,belong[idx])-size[r1];
301 if (tmp<0 && f[nowmid][0]) nowmid=f[nowmid][0];
302 int next=get_next(nowmid,p1);
303 get_change(nowmid,idx,next);
304 mid[r1]=nowmid;
305 ans^=ans1^ans2^mid[r1];
306 }
307 }
308 }
309
310 int main()
311 {
312 scanf("%d%d",&n,&m);
313 for (int a=1;a<=m;a++)
314 {
315 scanf("%s",s);
316 if (s[0]=='A')
317 {
318 q[a][0]=1;
319 scanf("%d%d",&q[a][1],&q[a][2]);
320 }
321 else
322 {
323 if (s[0]=='X') q[a][0]=2;
324 else
325 {
326 q[a][0]=3;
327 scanf("%d",&q[a][1]);
328 }
329 }
330 }
331 prepare();
332 solve();
333
334 return 0;
335 }
第二题:
这个…………网络流应该是很明显的…………问题就是建模吧…………
建模方式如下:
1、 首先将每个点的va、vb值乘二。
2、 将每条边的ea、eb值加上ec。
3、 将ans记为当前所有的va、vb之和加上每条边的三种权值之和。
4、 从源点向每个点连一条容量为va的边
5、 从每个点向汇点连一条容量为vb的边
6、 对于一条边,将起点和终点之间连一条容量为ea+eb+2*ec的边。
那么最后的答案即为(ans-最大流)/2。
至于这个模型的正确性,只需要考虑其割集的意义便可以很容易的证明了。(如果有人不懂的话我再把那个囧图画出来吧………………)
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<algorithm>
5
6 using namespace std;
7
8 #ifdef unix
9 #define LL "%lld"
10 #else
11 #define LL "%I64d"
12 #endif
13
14 const int maxn=10010*2;
15 const int maxm=40010*6;
16 const long long INF=123456789876543ll;
17
18 int n,m,en,s,t;
19
20 long long va[maxn],vb[maxn],ans,depth[maxn],q[maxn];
21
22 struct edge
23 {
24 int e;
25 long long f;
26 edge *next,*op;
27 }*v[maxn],ed[maxm];
28
29 void add_edge(int s,int e,long long f)
30 {
31 en++;
32 ed[en].next=v[s];v[s]=ed+en;v[s]->e=e;v[s]->f=f;
33 en++;
34 ed[en].next=v[e];v[e]=ed+en;v[e]->e=s;v[e]->f=0;
35 v[s]->op=v[e];v[e]->op=v[s];
36 }
37
38 bool bfs()
39 {
40 memset(depth,0,sizeof(depth));
41 depth[s]=1;
42 int front=1,tail=1;
43 q[front]=s;
44 while (front<=tail)
45 {
46 int now=q[front];
47 front++;
48 for (edge *e=v[now];e;e=e->next)
49 if (e->f && !depth[e->e])
50 {
51 depth[e->e]=depth[now]+1;
52 tail++;
53 q[tail]=e->e;
54 }
55 }
56 return depth[t]!=0;
57 }
58
59 long long dfs(int now,long long cur_flow)
60 {
61 if (now==t) return cur_flow;
62 long long rest=cur_flow;
63 for (edge *e=v[now];e;e=e->next)
64 if (e->f && rest && depth[e->e]>depth[now])
65 {
66 long long new_flow=dfs(e->e,min(e->f,rest));
67 e->f-=new_flow;
68 e->op->f+=new_flow;
69 rest-=new_flow;
70 }
71 if (cur_flow==rest) depth[now]=0;
72 return cur_flow-rest;
73 }
74
75 long long dinic()
76 {
77 long long ans=0;
78 while (bfs())
79 ans+=dfs(s,INF);
80 return ans;
81 }
82
83 int main()
84 {
85 scanf("%d%d",&n,&m);
86 for (int a=2;a<n;a++)
87 {
88 scanf(LL,&va[a]);
89 va[a]<<=1;
90 ans+=va[a];
91 }
92 for (int a=2;a<n;a++)
93 {
94 scanf(LL,&vb[a]);
95 vb[a]<<=1;
96 ans+=vb[a];
97 }
98 va[1]=INF;vb[n]=INF;
99 for (int a=1;a<=m;a++)
100 {
101 int s,e,ea,eb,ec;
102 scanf("%d%d%d%d%d",&s,&e,&ea,&eb,&ec);
103 ans+=(ea+eb+ec)<<1;
104 ea+=ec;
105 eb+=ec;
106 va[s]+=ea;va[e]+=ea;
107 vb[s]+=eb;vb[e]+=eb;
108 add_edge(s,e,ea+eb);
109 add_edge(e,s,ea+eb);
110 }
111 s=0;t=n+1;
112 for (int a=1;a<=n;a++)
113 add_edge(0,a,va[a]);
114 for (int a=1;a<=n;a++)
115 add_edge(a,n+1,vb[a]);
116 printf(LL "\n",(ans-dinic())>>1);
117
118 return 0;
119 }
第三题:
这题好像很水的样子啊………………
考虑有几行被选了奇数次,那么有多少列被选了奇数次也就确定了,这样一来的话就成了简单的组合计数问题了…………囧………………
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<algorithm>
5
6 using namespace std;
7
8 #ifdef unix
9 #define LL "%lld"
10 #else
11 #define LL "%I64d"
12 #endif
13
14 const int maxn=100010;
15 const int mo=1000000007;
16
17 int n,m,r,c,ans,v0[maxn],v1[maxn],v2[maxn],v3[maxn],v4[maxn];
18
19 long long s;
20
21 int multi(long long a,int b)
22 {
23 a*=b;
24 if (a>=mo) a%=mo;
25 return (int)a;
26 }
27
28 void inc(int &a,int b)
29 {
30 a+=b;
31 if (a>=mo) a-=mo;
32 }
33
34 int mul(int a,int b)
35 {
36 int ans=1;
37 while (b)
38 {
39 if (b&1) ans=multi(ans,a);
40 a=multi(a,a);
41 b>>=1;
42 }
43 return ans;
44 }
45
46 int main()
47 {
48 scanf("%d%d%d%d" LL,&n,&m,&r,&c,&s);
49 v1[0]=1;
50 for (int a=1;a<=100000;a++)
51 v0[a]=mul(a,mo-2);
52 int tmp=1;
53 for (int a=0;a<=(r>>1);a++)
54 {
55 v1[a]=tmp;
56 tmp=multi(tmp,multi(a+n,v0[a+1]));
57 }
58 tmp=1;
59 for (int a=0;a<=(c>>1);a++)
60 {
61 v2[a]=tmp;
62 tmp=multi(tmp,multi(a+m,v0[a+1]));
63 }
64 tmp=1;
65 for (int a=0;a<=n;a++)
66 {
67 v3[a]=tmp;
68 tmp=multi(tmp,multi(n-a,v0[a+1]));
69 }
70 tmp=1;
71 for (int a=0;a<=m;a++)
72 {
73 v4[a]=tmp;
74 tmp=multi(tmp,multi(m-a,v0[a+1]));
75 }
76 for (int a=r&1;a<=min(n,r);a+=2)
77 if (a*2!=n)
78 {
79 if (((s-(long long)a*m))%(n-a*2)) continue;
80 int b=(int)((s-(long long)a*m)/(n-a*2));
81 if (b>c || b<0 || ((c-b)&1)) continue;
82 int nowans=v3[a];
83 nowans=multi(nowans,v1[(r-a)>>1]);
84 nowans=multi(nowans,v4[b]);
85 nowans=multi(nowans,v2[(c-b)>>1]);
86 inc(ans,nowans);
87 }
88 else
89 {
90 if ((long long)a*m!=s) continue;
91 int nowans=v3[a];
92 nowans=multi(nowans,v1[(r-a)>>1]);
93 int cnt=0;
94 for (int b=(c&1);b<=min(r,c);b+=2)
95 inc(cnt,multi(v4[b],v2[(c-b)>>1]));
96 inc(ans,multi(ans,cnt));
97 }
98 printf("%d\n",ans);
99
100 return 0;
101 }
Day 5:
这天的题囧爆了………………
第一题:
由于有第一天第三题…………所以这题就太水了吧…………找出两个数列的循环节然后暴力乱搞就行了吧…………
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4
5 using namespace std;
6
7 #ifdef unix
8 #define LL "%lld"
9 #else
10 #define LL "%I64d"
11 #endif
12
13 const int maxn=200;
14 const long long mo=1000000007;
15 const long long mo2=mo*2+2;
16 const long long mo3=mo2*3;
17
18 char s[maxn];
19
20 struct matrix
21 {
22 long long z[3][3];
23 matrix()
24 {
25 memset(z,0,sizeof(z));
26 }
27 matrix operator*(const matrix &a)const
28 {
29 matrix ans;
30 for (int b=1;b<=2;b++)
31 for (int c=1;c<=2;c++)
32 for (int d=1;d<=2;d++)
33 ans.z[b][c]=(ans.z[b][c]+z[b][d]*a.z[d][c]%mo)%mo;
34 return ans;
35 }
36 matrix operator+(const matrix &a)const
37 {
38 matrix ans;
39 for (int b=1;b<=2;b++)
40 for (int c=1;c<=2;c++)
41 for (int d=1;d<=2;d++)
42 ans.z[b][c]=(ans.z[b][c]+z[b][d]*a.z[d][c]%mo2)%mo2;
43 return ans;
44 }
45 }m1,m2;
46
47 struct bign
48 {
49 int z[maxn],l;
50 void init()
51 {
52 memset(z,0,sizeof(z));
53 scanf("%s",s+1);
54 l=strlen(s+1);
55 for (int a=1;a<=l;a++)
56 z[a]=s[l-a+1]-'0';
57 }
58 long long operator%(const long long &a)const
59 {
60 long long b=0;
61 for (int c=l;c>=1;c--)
62 b=(b*10+z[c])%a;
63 return b;
64 }
65 }z;
66
67 long long get(long long v)
68 {
69 if (v==0) return 0;
70 m1.z[1][1]=0;
71 m1.z[1][2]=1;
72 m2.z[1][1]=0;
73 m2.z[1][2]=m2.z[2][1]=m2.z[2][2]=1;
74 while (v)
75 {
76 if (v&1) m1=m1*m2;
77 m2=m2*m2;
78 v>>=1;
79 }
80 return m1.z[1][1];
81 }
82
83 long long get1(long long v)
84 {
85 if (v==0) return 0;
86 m1.z[1][1]=0;
87 m1.z[1][2]=1;
88 m2.z[1][1]=0;
89 m2.z[1][2]=m2.z[2][1]=m2.z[2][2]=1;
90 while (v)
91 {
92 if (v&1) m1=m1+m2;
93 m2=m2+m2;
94 v>>=1;
95 }
96 return m1.z[1][1];
97 }
98
99 int main()
100 {
101 int t;
102 scanf("%d",&t);
103 for (int a=1;a<=t;a++)
104 {
105 z.init();
106 long long v1=z % mo3;
107 v1=get1(v1);
108 printf(LL "\n",get(v1));
109 }
110
111 return 0;
112 }
第二题:
题解上说暴力打张表出来…………囧…………
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4
5 using namespace std;
6
7 char s[2600]="3141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420198938095257201065485863278";
8
9 int n;
10
11 int main()
12 {
13 scanf("%d",&n);
14 for (int a=0;a<n;a++)
15 printf("%c",s[a]);
16 printf("\n");
17
18 return 0;
19 }
第三题:
考虑10^10=2^10*5^10,那么只要分别搞出答案对这两个数的余数然后用中国剩余定理就搞定了(其实这里可以暴力………………)
我们这里只考虑C(n,m) mod 2^10的情况,等价于n!/m!/(n-m)! mod 2^10,很明显我们在算的时候需要把2这个约数单独拿出来考虑,那么问题变成了设n!=x*2^y,x是一个奇数,求x mod 2^10,我们先把n=1024的情况暴力出来之后,然后我们发现不考虑偶数的话每1024个数的情况都是一样的,这个就可以很快的求出来了。然后我们考虑偶数,首先把每个偶数都除以二,我们发现这个问题就转化成了一个规模更小的原问题,那么递归处理下去就可以了。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4
5 using namespace std;
6
7 #ifdef unix
8 #define LL "%lld"
9 #else
10 #define LL "%I64d"
11 #endif
12
13 #ifdef unix
14 #define LX "%010lld"
15 #else
16 #define LX "%010I64d"
17 #endif
18
19 const long long mo=10000000000ll;
20 const long long mo1=1024;
21 const long long mo2=9765625;
22
23 long long n,m,v[mo2];
24
25 long long get(long long a,int b)
26 {
27 long long ans=0;
28 while (a)
29 {
30 ans+=a/b;
31 a/=b;
32 }
33 return ans;
34 }
35
36 long long mul(long long a,long long b,long long mo)
37 {
38 long long ans=1;
39 while (b)
40 {
41 if (b&1) ans=ans*a%mo;
42 a=a*a%mo;
43 b>>=1;
44 }
45 return ans;
46 }
47
48 long long fac(long long n,long long div,long long mo)
49 {
50 long long nowans=1;
51 if (n>=div) nowans=fac(n/div,div,mo);
52 return nowans*(mul(v[mo-1],n/mo,mo)*v[n%mo]%mo)%mo;
53 }
54
55 long long calc(long long n,long long m,long long div)
56 {
57 int sum;
58 if ((sum=get(n,div)-get(m,div)-get(n-m,div))>=10) return 0;
59 long long mo;
60 if (div==2) mo=mo1;
61 else mo=mo2;
62 v[0]=1;
63 for (int a=1;a<mo;a++)
64 v[a]=v[a-1]*(a%div?a:1)%mo;
65 return fac(n,div,mo)*mul(fac(m,div,mo)*fac(n-m,div,mo)%mo,mo/div*(div-1)-1,mo)%mo*mul(div,sum,mo)%mo;
66 }
67
68
69 int main()
70 {
71
72 scanf(LL LL,&n,&m);
73 if (n<m)
74 {
75 printf("0000000000\n");
76 return 0;
77 }
78 long long v1=calc(n,m,2);
79 long long v2=calc(n,m,5);
80 while (v2%mo1!=v1)
81 v2+=mo2;
82 printf(LX "\n",v2);
83
84 return 0;
85 }
Day 6:
第一题:
我们把这个问题离线来做,我们对B树的每一个点从它自己到根的字符串按照字典序排个序。
然后开始处理询问,分两种情况:
1、 对A树添加一个节点:
考虑新节点的父亲,如果我们对新节点的父亲在B那个已经排序好的数组里记录了一个可匹配的区间,那么新的节点在那个数组中能够匹配的区间一定是被父亲的区间所包含的,且我们在找这个区间时只需要匹配新加入的这个字母。于是我们在这个区间里二分上下界得到一个新的区间,那么就处理出了新节点所对应的一个区间。然后这种操作对答案的影响便是这个区间内有多少个B的串已被加入,这个可以在没次加入一个B中的结点时用树状数组维护即可。
2、 对B树添加一个结点:
考虑这种操作对答案的影响,它能带来的影响就是以前有多少个A树中的结点所对应的区间包含了它,那么这个也可以用一个树状数组维护即可。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<algorithm>
5
6 using namespace std;
7
8 #ifdef unix
9 #define LL "%lld"
10 #else
11 #define LL "%I64d"
12 #endif
13
14 #define lb(p) (p)&(-(p))
15
16 const int maxn=100010;
17 const unsigned long long base=10003;
18
19 int n,q[maxn][3],z1[maxn],z2[maxn],f[maxn][20],fe[maxn],cnt2,size1,size2,sa[maxn],rank[maxn],depth1[maxn],depth2[maxn];
20
21 long long ans;
22
23 unsigned long long bit[maxn],h[maxn];
24
25 pair<int,int> range[maxn];
26
27 void change(int *z,int p,int delta)
28 {
29 while (p<=cnt2)
30 {
31 z[p]+=delta;
32 p+=lb(p);
33 }
34 }
35
36 int query(int *z,int p)
37 {
38 int ans=0;
39 while (p)
40 {
41 ans+=z[p];
42 p-=lb(p);
43 }
44 return ans;
45 }
46
47 bool cmp(int a,int b)
48 {
49 int x=0;
50 while ((1<<x)<depth2[a] && (1<<x)<depth2[b])
51 x++;
52 for (x--;x>=0;x--)
53 {
54 unsigned long long h1=h[a]-h[f[a][x]]*bit[1<<x];
55 unsigned long long h2=h[b]-h[f[b][x]]*bit[1<<x];
56 if (h1==h2) a=f[a][x],b=f[b][x];
57 }
58 return fe[a]<fe[b];
59 }
60
61 int up(int p,int s)
62 {
63 int x=0;
64 while (s)
65 {
66 if (s&1) p=f[p][x];
67 s>>=1;
68 x++;
69 }
70 return p;
71 }
72
73 int main()
74 {
75
76 scanf("%d",&n);
77 bit[0]=1;
78 for (int a=1;a<=n;a++)
79 bit[a]=bit[a-1]*base;
80 depth1[1]=depth2[1]=1;
81 cnt2=1;
82 for (int a=1;a<=n;a++)
83 {
84 int t,v;
85 char s[2],c;
86 scanf("%d%d%s",&t,&v,s);
87 c=s[0];
88 q[a][0]=t;q[a][1]=v;q[a][2]=c;
89 if (t==2)
90 {
91 cnt2++;
92 depth2[cnt2]=depth2[v]+1;
93 fe[cnt2]=c;
94 h[cnt2]=h[v]*base+c-'a'+1;
95 f[cnt2][0]=v;
96 int x=0;
97 while (f[v][x])
98 {
99 f[cnt2][x+1]=f[v][x];
100 v=f[v][x];
101 x++;
102 }
103 }
104 }
105 for (int a=1;a<=cnt2;a++)
106 sa[a]=a;
107 sort(sa+1,sa+cnt2+1,cmp);
108 for (int a=1;a<=cnt2;a++)
109 rank[sa[a]]=a;
110 size1=size2=1;
111 range[1]=make_pair(1,cnt2+1);
112 for (int a=1;a<=n;a++)
113 {
114 if (q[a][0]==1)
115 {
116 size1++;
117 depth1[size1]=depth1[q[a][1]]+1;
118 if (range[q[a][1]].first!=-1)
119 {
120 int lower=range[q[a][1]].first,upper=range[q[a][1]].second-1;
121 int l=lower,r=upper;
122 while (l<=r)
123 {
124 int m=(l+r)>>1;
125 if (fe[up(sa[m],depth1[size1]-2)]<q[a][2]) l=m+1;
126 else r=m-1;
127 }
128 int new_lower=l;
129 l=lower,r=upper;
130 while (l<=r)
131 {
132 int m=(l+r)>>1;
133 if (fe[up(sa[m],depth1[size1]-2)]>q[a][2]) r=m-1;
134 else l=m+1;
135 }
136 int new_upper=r+1;
137 if (new_lower>=new_upper) range[size1]=make_pair(-1,-1);
138 else
139 {
140 ans+=query(z2,new_upper-1)-query(z2,new_lower-1);
141 change(z1,new_lower,1);
142 change(z1,new_upper,-1);
143 range[size1]=make_pair(new_lower,new_upper);
144 }
145 }
146 else range[size1]=make_pair(-1,-1);
147 }
148 else
149 {
150 size2++;
151 change(z2,rank[size2],1);
152 ans+=query(z1,rank[size2]);
153 }
154 printf(LL "\n",ans+size2);
155 }
156
157 return 0;
158 }
第二题:
最暴力的dp很好做…………但是会T…………正确的做法要用stirling数进行优化…………但是我不会………………
(暂无)
第三题:
这题更不会了…………只知道是树链剖分套动态树…………以后有时间的时候再来说这道题吧………………
(暂无)
Day 7:
第一题:
这题很水,我们设定一个参数t,将度数大于t的设为大点,将剩下的点设为小点。然后我们对大点小点分开做,每次采用不同的方法暴力就行了。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<cmath>
5 #include<algorithm>
6
7 using namespace std;
8
9 #ifdef unix
10 #define LL "%lld"
11 #else
12 #define LL "%I64d"
13 #endif
14
15 const int maxn=100010;
16 const int maxm=100010;
17
18 int n,m,q,en,l,bigp[maxn],col[maxn],in[maxn];
19
20 long long sum[2][2],cost[maxn][2];
21
22 bool tag[maxn];
23
24 char s[8];
25
26 struct edge
27 {
28 int e;
29 long long d;
30 edge *next;
31 }*v[maxn],ed[maxm<<1];
32
33 struct edg
34 {
35 int s,e;
36 long long d;
37 bool operator==(const edg &a)const
38 {
39 return s==a.s && e==a.e;
40 }
41 bool operator<(const edg &a)const
42 {
43 if (s==a.s) return e<a.e;
44 else return s<a.s;
45 }
46 }e[maxm];
47
48 void add_edge(int s,int e,long long d)
49 {
50 en++;
51 ed[en].next=v[s];v[s]=ed+en;v[s]->e=e;v[s]->d=d;
52 }
53
54 int main()
55 {
56 scanf("%d%d",&n,&m);
57 for (int a=1;a<=n;a++)
58 scanf("%d",&col[a]);
59 for (int a=1;a<=m;a++)
60 {
61 scanf("%d%d" LL,&e[a].s,&e[a].e,&e[a].d);
62 if (e[a].s>e[a].e) swap(e[a].s,e[a].e);
63 if (e[a].s==0) printf("%d\n",a);
64 }
65 sort(e+1,e+m+1);
66 int last=1;
67 for (int a=2;a<=m;a++)
68 if (e[a]==e[last]) e[last].d+=e[a].d,tag[a]=true;
69 else last=a;
70 for (int a=1;a<=m;a++)
71 if (!tag[a]) in[e[a].s]++,in[e[a].e]++;
72 int maxsize=(int)exp(log(n)*2/3);
73 //printf("%d\n",maxsize);
74 for (int a=1;a<=m;a++)
75 if (!tag[a])
76 {
77 // printf("%d %d %I64d\n",e[a].s,e[a].e,e[a].d);
78 if (in[e[a].s]>maxsize)
79 {
80 add_edge(e[a].e,e[a].s,e[a].d);
81 if (in[e[a].e]>maxsize)
82 {
83 add_edge(e[a].s,e[a].e,e[a].d);
84 sum[min(col[e[a].s],col[e[a].e])][max(col[e[a].s],col[e[a].e])]+=e[a].d;
85 }
86 else cost[e[a].s][col[e[a].e]]+=e[a].d;
87 }
88 else
89 {
90 add_edge(e[a].s,e[a].e,e[a].d);
91 if (in[e[a].e]>maxsize) cost[e[a].e][col[e[a].s]]+=e[a].d;
92 else
93 {
94 add_edge(e[a].e,e[a].s,e[a].d);
95 sum[min(col[e[a].s],col[e[a].e])][max(col[e[a].s],col[e[a].e])]+=e[a].d;
96 }
97 }
98 }
99 for (int a=1;a<=n;a++)
100 if (in[a]>maxsize)
101 {
102 l++;
103 bigp[l]=a;
104 }
105 //printf("%I64d %I64d %I64d\n",sum[0][0],sum[0][1],sum[1][1]);
106 //for (int a=1;a<=n;a++)
107 // printf("%I64d %I64d\n",cost[a][0],cost[a][1]);
108 scanf("%d",&q);
109 for (int a=1;a<=q;a++)
110 {
111 scanf("%s",s);
112 if (s[0]=='A')
113 {
114 int c1,c2;
115 scanf("%d%d",&c1,&c2);
116 long long ans=sum[min(c1,c2)][max(c1,c2)];
117 for (int b=1;b<=l;b++)
118 {
119 int newc=c1+c2-col[bigp[b]];
120 if (newc==0 || newc==1) ans+=cost[bigp[b]][newc];
121 }
122 printf(LL "\n",ans);
123 }
124 else
125 {
126 int p;
127 scanf("%d",&p);
128 if (in[p]>maxsize)
129 {
130 for (edge *e=v[p];e;e=e->next)
131 {
132 sum[min(col[p],col[e->e])][max(col[p],col[e->e])]-=e->d;
133 sum[min(col[p]^1,col[e->e])][max(col[p]^1,col[e->e])]+=e->d;
134 }
135 }
136 else
137 {
138 for (edge *e=v[p];e;e=e->next)
139 if (in[e->e]>maxsize)
140 {
141 cost[e->e][col[p]]-=e->d;
142 cost[e->e][col[p]^1]+=e->d;
143 }
144 else
145 {
146 sum[min(col[p],col[e->e])][max(col[p],col[e->e])]-=e->d;
147 sum[min(col[p]^1,col[e->e])][max(col[p]^1,col[e->e])]+=e->d;
148 }
149 }
150 col[p]^=1;
151 }
152 }
153
154 return 0;
155 }
第二题:
首先解决周长为n的三角形个数有多少,用f(n)表示,考虑用a、b、c表示三条边并且有a<=b<=c,那么考虑以下两种情况:
1、b=c,那么这个时候a+b>c,可以直接算出答案。
2、b<c,那么这个时候一定有a+b>=c+1,所以可以利用f(n-1)的值推过来,在算一下a+b=c+1的情况就行了。
然后考虑用g(n)表示周长为n且三边gcd为1的三角形个数,那么考虑排除gcd不为1的情况,这个只需要枚举其gcd为多少然后减去相应的值即可。
然后考虑我们需要算出相似的组数,那么考虑所有的相似三角形一定都是基于一个周长然后乘上了某个值,那么我们只需要枚举这个基础周长,然后利用组合公式算一算就行了。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<algorithm>
5
6 using namespace std;
7
8 const int maxn=5000010;
9 const int mo=1000000007;
10
11 int n,cnt,f[maxn],er[maxn],divisor[maxn];
12
13 #define inc(a,b) a+=b,(a>=mo ? a-=mo : 0)
14
15 int main()
16 {
17 scanf("%d",&n);
18 for (int a=2;a<=n;a++)
19 f[a]=f[a-2],inc(f[a],a/3-a/4);
20 for (int a=1;a*a<=n;a++)
21 if (n % a==0)
22 {
23 cnt++;
24 divisor[cnt]=a;
25 if (a*a!=n)
26 {
27 cnt++;
28 divisor[cnt]=n/a;
29 }
30 }
31 sort(divisor+1,divisor+cnt+1);
32 for (int a=1;a<=cnt;a++)
33 for (int b=1;b<a;b++)
34 if (divisor[a] % divisor[b]==0) inc(f[divisor[a]],mo-f[divisor[b]]);
35 er[0]=1;
36 for (int a=1;a<=n;a++)
37 er[a]=er[a-1],inc(er[a],er[a-1]);
38 int ans=0;
39 for (int a=1;a<=cnt;a++)
40 inc(ans,(long long)f[divisor[a]]*er[n/divisor[a]-1]%mo);
41 printf("%d\n",ans);
42
43 return 0;
44 }
第三题:
此题即为暴力。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4
5 using namespace std;
6
7 long long n;
8
9 #ifdef unix
10 #define LL "%lld"
11 #else
12 #define LL "%I64d"
13 #endif
14
15 int main()
16 {
17
18 scanf(LL,&n);
19 long long ans=0,tmp=0;
20 for (long long a=1,v;a*a<=(v=n/a);a++,ans++)
21 for (long long b=a+1;b*b<=v;b++)
22 tmp+=n/(a*b)-b;
23 ans+=tmp*6;
24 tmp=0;
25 for (long long a=1,v;(v=a*a)<=n;a++)
26 {
27 tmp+=n/v;
28 if (a*a<=n/a) tmp--;
29 }
30 ans+=tmp*3;
31 printf(LL "\n",ans);
32
33 return 0;
34 }
Day 8:
第一题:
这题是大水题啊,推一下然后把矩阵搞出来然后快速幂就行了。注意如果推出来的矩阵比较大需要压缩矩阵,不然会T。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4
5 using namespace std;
6
7 #ifdef unix
8 #define LL "%lld"
9 #else
10 #define LL "%I64d"
11 #endif
12
13 #define inc(a,b) (a+=b,(a>=mo ? a-=mo : 0))
14
15 long long k,A,B,C,D,n,mo;
16
17 struct matrix
18 {
19 long long z[3][3];
20 matrix()
21 {
22 memset(z,0,sizeof(z));
23 }
24 void init()
25 {
26 memset(z,0,sizeof(z));
27 }
28 matrix operator*(const matrix &a)const
29 {
30 static matrix ans;
31 ans.init();
32 for (int b=1;b<=2;b++)
33 for (int c=1;c<=2;c++)
34 for (int d=1;d<=2;d++)
35 inc(ans.z[b][c],z[b][d]*a.z[d][c]%mo);
36 return ans;
37 }
38 }m1,m2;
39
40 void gedit(long long &v)
41 {
42 v%=mo;
43 if (v<0) v+=mo;
44 }
45
46 long long mul(long long a,long long b)
47 {
48 long long ans=1;
49 while (b)
50 {
51 if (b & 1) ans=ans*a%mo;
52 a=a*a%mo;
53 b>>=1;
54 }
55 return ans;
56 }
57
58 int main()
59 {
60 int t;
61 scanf("%d",&t);
62 for (int p=1;p<=t;p++)
63 {
64 scanf(LL LL LL LL LL LL LL,&k,&A,&B,&C,&D,&n,&mo);
65 gedit(k);
66 gedit(A);
67 gedit(B);
68 gedit(C);
69 gedit(D);
70 if (n==0) printf(LL "\n",k);
71 else
72 {
73 n--;
74 m1.init();m2.init();
75 m1.z[1][1]=A;m1.z[1][2]=C;
76 m1.z[2][1]=B;m1.z[2][2]=D;
77 m2=m1;
78 while (n)
79 {
80 if (n&1) m1=m1*m2;
81 m2=m2*m2;
82 n>>=1;
83 }
84 long long up=(m1.z[1][1]*k%mo+m1.z[2][1])%mo;
85 long long down=(m1.z[1][2]*k%mo+m1.z[2][2])%mo;
86 printf(LL "\n",up*mul(down,mo-2)%mo);
87 }
88 }
89
90 return 0;
91 }
第二题:
这题比较神,我们从低位向高位进行dp,没做到一位的时候进行一次排序,将这些数按照后k位的大小进行排序。然后我们考虑我们要减去的那个数的后k位在哪个区间,那么这样我们就可以算出减去这个数后会向高位借多少位,那么每次枚举这个区间就行了。感觉这个思路简直太神了。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4
5 using namespace std;
6
7 #ifdef unix
8 #define LL "%lld"
9 #else
10 #define LL "%I64d"
11 #endif
12
13 const int maxn=200010;
14
15 int n,delta[maxn];
16
17 long long f[70][maxn],z[maxn],sw[maxn];
18
19 int main()
20 {
21
22 scanf("%d",&n);
23 for (int a=1;a<=n;a++)
24 scanf(LL,&z[a]);
25 f[0][0]=1;
26 for (int a=0;a<=63;a++)
27 {
28 int l1=0,l2=0;
29 memset(delta,0,sizeof(delta));
30 for (int b=1;b<=n;b++)
31 if (((z[b]>>a)&1)==0) l2++;
32 for (int b=1;b<=n;b++)
33 if (((z[b]>>a)&1)==0)
34 {
35 l1++;
36 sw[l1]=z[b];
37 delta[b]=delta[b-1]+1;
38 }
39 else
40 {
41 l2++;
42 sw[l2]=z[b];
43 delta[b]=delta[b-1];
44 }
45 for (int b=1;b<=n;b++)
46 z[b]=sw[b];
47 for (int b=0;b<=n;b++)
48 if (f[a][b])
49 {
50 if ((((n-delta[n])^b)&1)==0) f[a+1][delta[b]]+=f[a][b];
51 if ((((n-delta[n])^(n-b))&1)==0) f[a+1][delta[n]+b-delta[b]]+=f[a][b];
52 }
53 }
54 for (int a=2;a<=n;a++)
55 z[0]^=z[a]-z[1];
56 if (z[0]==0) f[64][0]--;
57 printf(LL "\n",f[64][0]);
58
59 return 0;
60 }
第三题:
把式子展开之后就可以发现只有一项特别囧的无法维护,考试的时候我就暴力加一点优化结果有80分…………囧…………
如果我们把两个矩阵都写成一行的形式,那么我们就可以发现我们要维护的那个东西就是很类似于高精度乘法的一个东西,于是FFT优化即可,但我不会写FFT…………囧………………
(暂无)
Day 9:
第一题:
这题是搞笑的吧…………一开始我想用后缀数组加主席树来做…………发现不就是搞笑题吗…………
对模式串建一棵trie,然后每个结点上记录有哪些编号的串能够到达这里,看起来这东西很大,其实空间和时间复杂度都是O(n)的,然后对于每一个询问串暴力走就行了。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<algorithm>
5 #include<vector>
6
7 using namespace std;
8
9 const int maxn=100010;
10
11 int n,m,z[maxn];
12
13 char s[maxn];
14
15 struct node
16 {
17 node *next[26];
18 vector<int> id;
19 node()
20 {
21 memset(next,0,sizeof(next));
22 }
23 }*root,*p,wmt[maxn],*wmtwmt=wmt;
24
25 node *newnode()
26 {
27 return wmtwmt++;
28 }
29
30 bool cmp(int a,int b)
31 {
32 return z[a]>z[b];
33 }
34
35 void insert(int idx)
36 {
37 scanf("%s",s);
38 int l=strlen(s);
39 p=root;
40 for (int a=0;a<l;a++)
41 {
42 if (p->next[s[a]-'a']==NULL) p->next[s[a]-'a']=newnode();
43 p=p->next[s[a]-'a'];
44 if (!p->id.size() || p->id.back()!=idx) p->id.push_back(idx);
45 }
46 }
47
48 void dfs(node *p)
49 {
50 sort(p->id.begin(),p->id.end(),cmp);
51 for (int a=0;a<26;a++)
52 if (p->next[a]) dfs(p->next[a]);
53 }
54
55 void query()
56 {
57 int k;
58 scanf("%d%s",&k,s);
59 int l=strlen(s);
60 p=root;
61 for (int a=0;a<l;a++)
62 {
63 if (p->next[s[a]-'a']==NULL)
64 {
65 printf("0\n");
66 return;
67 }
68 p=p->next[s[a]-'a'];
69 }
70 printf("%d ",(int)p->id.size());
71 int upper=min(k,(int)p->id.size());
72 for (int a=0;a<upper;a++)
73 printf("%d ",p->id[a]-1);
74 printf("\n");
75 }
76
77 int main()
78 {
79
80 scanf("%d%d",&n,&m);
81 root=newnode();
82 for (int a=1;a<=n;a++)
83 {
84 scanf("%d",&z[a]);
85 int num;
86 scanf("%d",&num);
87 for (int b=1;b<=num;b++)
88 insert(a);
89 }
90 dfs(root);
91 for (int a=1;a<=m;a++)
92 query();
93
94 return 0;
95 }
第二题:
水DP,先算出到每个点不删边的答案,然后枚举删掉哪条边,然后算出答案即可。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<algorithm>
5 #include<cmath>
6
7 using namespace std;
8
9 const int maxn=10010;
10 const int maxm=100010;
11
12 int n,m,en,l,z[maxn],sum[maxn],in[maxn];
13
14 long double f[maxn],p[maxn];
15
16 struct edge
17 {
18 int e,d;
19 edge *next;
20 }*v[maxn],ed[maxm];
21
22 void add_edge(int s,int e,int d)
23 {
24 en++;
25 ed[en].next=v[s];v[s]=ed+en;v[s]->e=e;v[s]->d=d;
26 }
27
28 int main()
29 {
30 scanf("%d%d",&n,&m);
31 for (int a=1;a<=m;a++)
32 {
33 int s,e,d;
34 scanf("%d%d%d",&s,&e,&d);
35 s++;e++;
36 add_edge(s,e,d);
37 sum[s]+=d;
38 in[e]++;
39 }
40 for (int a=1;a<=n;a++)
41 if (!in[a])
42 {
43 l++;
44 z[l]=a;
45 }
46 for (int a=1;a<=n;a++)
47 for (edge *e=v[z[a]];e;e=e->next)
48 {
49 in[e->e]--;
50 if (!in[e->e])
51 {
52 l++;
53 z[l]=e->e;
54 }
55 }
56 p[1]=1;
57 for (int a=1;a<=n;a++)
58 for (edge *e=v[z[a]];e;e=e->next)
59 p[e->e]+=p[z[a]]*e->d/sum[z[a]];
60 for (int a=n;a>=1;a--)
61 for (edge *e=v[z[a]];e;e=e->next)
62 f[z[a]]+=(f[e->e]+1)*e->d/sum[z[a]];
63 long double ans=f[1];
64 for (int a=1;a<=n;a++)
65 for (edge *e=v[z[a]];e;e=e->next)
66 if (e->d!=sum[z[a]]) ans=max(ans,f[1]+p[z[a]]*((f[z[a]]-(f[e->e]+1)*e->d/sum[z[a]])*sum[z[a]]/(sum[z[a]]-e->d)-f[z[a]]));
67 printf("%.6lf\n",(double)ans);
68
69 return 0;
70 }
第三题:
这题怎么乱搞都行啊………………不过数据好像有问题…………囧…………
(暂无)
Day 10:
第一题:
考虑从每个点开始会有四个方向,我们只考虑其中某一个方向,比如说朝右下,这个时候我们可以发现这条直线上的x+y值是不变的,那么我们只需要按照x+y为第一关键字排个序,就能知道每个点朝右下走下一次会到哪一个点上去,那么对每个方向都做一个这样的处理。然后每次我们从每个点暴力向外走,由于每个点最多只会被经过两次,那么暴力走的这一块的复杂度是O(n)的。
(暂无)
第二题:
考虑最上面、最下面、最左面、左右面右4个顶点,而我们只有三个正方形,那么一定会有一个正方形覆盖到整个大矩形的一个角。有了这个性质之后就很好做了,我们首先二分答案,然后枚举第一个矩形放在了哪个角,然后剩下两个矩形一样的做法就可以了。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<algorithm>
5
6 using namespace std;
7
8 const int maxn=100010;
9 const int INF=0x3f3f3f3f;
10
11 int n,minx=INF,miny=INF,maxx=-INF,maxy=-INF,x[maxn],y[maxn],in[maxn];
12
13 bool inside(int x1,int y1,int r,int x2,int y2)
14 {
15 return (x2>=x1) && (x2<=x1+r) && (y2>=y1) && (y2<=y1+r);
16 }
17
18 bool check_tap2(int xx,int yy,int r)
19 {
20 for (int a=1;a<=n;a++)
21 if (inside(xx,yy,r,x[a],y[a])) in[a]++;
22 int x1=INF,x2=-INF,y1=INF,y2=-INF;
23 for (int a=1;a<=n;a++)
24 if (!in[a])
25 {
26 x1=min(x1,x[a]);
27 y1=min(y1,y[a]);
28 x2=max(x2,x[a]);
29 y2=max(y2,y[a]);
30 }
31 for (int a=1;a<=n;a++)
32 if (inside(xx,yy,r,x[a],y[a])) in[a]--;
33 if (x2-x1<=r && y2-y1<=r) return true;
34 return false;
35 }
36
37 bool check_tap(int xx,int yy,int r)
38 {
39 for (int a=1;a<=n;a++)
40 if (inside(xx,yy,r,x[a],y[a])) in[a]++;
41 int x1=INF,x2=-INF,y1=INF,y2=-INF;
42 for (int a=1;a<=n;a++)
43 if (!in[a])
44 {
45 x1=min(x1,x[a]);
46 y1=min(y1,y[a]);
47 x2=max(x2,x[a]);
48 y2=max(y2,y[a]);
49 }
50 if (x1==INF)
51 {
52 for (int a=1;a<=n;a++)
53 if (inside(xx,yy,r,x[a],y[a])) in[a]--;
54 return true;
55 }
56 bool able=check_tap2(x1,y1,r) || check_tap2(x1,y2-r,r) || check_tap2(x2-r,y1,r) || check_tap2(x2-r,y2-r,r);
57 for (int a=1;a<=n;a++)
58 if (inside(xx,yy,r,x[a],y[a])) in[a]--;
59 return able;
60 }
61
62 bool check(int r)
63 {
64 if (check_tap(minx,miny,r) || check_tap(minx,maxy-r,r) || check_tap(maxx-r,miny,r) || check_tap(maxx-r,maxy-r,r)) return true;
65 else return false;
66 }
67
68 int main()
69 {
70 scanf("%d",&n);
71 for (int a=1;a<=n;a++)
72 {
73 scanf("%d%d",&x[a],&y[a]);
74 minx=min(minx,x[a]);
75 miny=min(miny,y[a]);
76 maxx=max(maxx,x[a]);
77 maxy=max(maxy,y[a]);
78 }
79 int l=0,r=1000000000;
80 while (l+1!=r)
81 {
82 int m=(l+r)>>1;
83 if (check(m)) r=m;
84 else l=m;
85 }
86 printf("%d\n",r);
87
88 return 0;
89 }
第三题:
考虑会出现冲突的情况只有s1<s2 and t1>s2 and t1<t2这一种情况,那么我们…………………………………………………………………………………………………………
(暂无)
(关于有部分还没有的东西,以后会慢慢更新的…………)