bzoj [ 2017省队十连测推广赛1 ] ( 4765 && 4766 && 4767 )题解

bzoj 4765 -- 分块+dfs序+树状数组:

考虑分块。将1~n分成sqrt(n)块,对每个点记录它在每个块中的祖先个数,修改一个点时枚举每一块修改。

查询[l,r]时如果一个块在[l,r]中,直接将其加入答案。显然只剩下O(sqrt(n))个点。求出树的dfs序,用树状数组维护就可以O(logn)求出答案。

时间复杂度O(n*sqrt(n)*logn)

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 using namespace std;
 6 inline char nc(){
 7     static char buf[100000],*p1=buf,*p2=buf;
 8     if(p1==p2){
 9         p2=(p1=buf)+fread(buf,1,100000,stdin);
10         if(p1==p2)return EOF;
11     }
12     return *p1++;
13 }
14 inline void Read(int& x){
15     char c=nc();
16     for(;c<'0'||c>'9';c=nc());
17     for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());
18 }
19 #define ULL unsigned long long
20 #define ll long long
21 #define N 100010
22 #define SN 400
23 #define lowbit(x) x&-x
24 struct Edge{
25     int t,nx;
26 }e[N<<1];
27 ULL Sum[SN],c[N],p[N];
28 ll k;
29 int i,j,n,m,Cnt,s[N][SN],t[SN],S,T,a[N],b[N],f[N],h[N],B[N],Num,Rt,x,y,z;
30 inline void Add(int x,int y){
31     e[++Num].t=y;e[Num].nx=h[x];h[x]=Num;
32 }
33 inline void Update_c(int x,int y){
34     for(;x<=n;x+=lowbit(x))c[x]+=y;
35 }
36 inline void Dfs(int x,int F){
37     p[x]=a[x];b[x]=++m;if(a[x])Update_c(b[x],a[x]);t[B[x]]++;
38     for(int i=1;i<=Cnt;i++)s[x][i]=t[i];
39     for(int i=h[x];i;i=e[i].nx)
40     if(e[i].t!=F){
41         Dfs(e[i].t,x);
42         p[x]+=p[e[i].t];
43     }
44     Sum[B[x]]+=p[x];f[x]=m;t[B[x]]--;
45 }
46 inline void Update(int x,int y){
47     for(int i=1;i<=Cnt;i++)Sum[i]+=1ll*y*s[x][i];
48     Update_c(b[x],y);
49 }
50 inline ULL Query_c(int l,int r){
51     ULL Ans=0;
52     for(int i=r;i;i-=lowbit(i))Ans+=c[i];
53     for(int i=l-1;i;i-=lowbit(i))Ans-=c[i];
54     return Ans;
55 }
56 inline ULL Query(int l,int r){
57     ULL Ans=0;
58     if(B[l]==B[r]){
59         for(int i=l;i<=r;i++)Ans+=Query_c(b[i],f[i]);
60         return Ans;
61     }
62     for(int i=B[l]+1;i<B[r];i++)Ans+=Sum[i];
63     for(int i=l;B[i]==B[l];i++)Ans+=Query_c(b[i],f[i]);
64     for(int i=r;B[i]==B[r];i--)Ans+=Query_c(b[i],f[i]);
65     return Ans;
66 }
67 char ss[40];
68 int Len;
69 inline void Print(ULL x){
70     if(x==0)putchar(48);
71     for(Len=0;x;x/=10)ss[++Len]=x%10;
72     for(;Len;)putchar(ss[Len--]+48);putchar('\n');
73 }
74 int main()
75 {
76     Read(n);Read(T);S=sqrt((double)n);
77     for(i=1;i<=n;i++)B[i]=(i-1)/S+1;Cnt=B[n];
78     for(i=1;i<=n;i++)Read(a[i]);
79     for(i=1;i<=n;i++){
80         Read(x);Read(y);
81         if(x==0)Rt=y;else Add(x,y),Add(y,x);
82     }
83     Dfs(Rt,0);
84     while(T--){
85         Read(z);Read(x);Read(y);
86         if(z&1)Update(x,y-a[x]),a[x]=y;else Print(Query(x,y));
87     }
88     return 0;
89 }
bzoj4765


bzoj 4766 -- 快速幂+快速乘

一个结论:

一个完全二分图的生成树个数为nm-1*mn-1

由于要爆long long,要用快速乘。

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 #define ll long long
 6 ll n,m,p,a,b;
 7 inline ll Ch(ll x,ll y){
 8     ll Ans=0;
 9     while(y){
10         if(y&1)Ans=(Ans+x)%p;
11         x=(x<<1)%p;
12         y>>=1;
13     }
14     return Ans;
15 }
16 inline ll Pow(ll x,ll y){
17     if(y==0)return 1;
18     ll Ans=Pow(x,y>>1);
19     if(y&1)return Ch(Ch(Ans,Ans),x);
20     return Ch(Ans,Ans);
21 }
22 int main()
23 {
24     scanf("%lld%lld%lld",&n,&m,&p);
25     a=Pow(n,m-1);b=Pow(m,n-1);
26     printf("%lld",Ch(a,b));
27 }
bzoj4766


bzoj4767 -- DP+容斥

对于每个障碍点,从原点到它要使用的两种操作的次数可以用解方程解出。

对于一个障碍点,假如它需要的操作次数分别是x,y,那么从原点到它的路径种数就是(x+y)!/(x!*y!)

将每个障碍点需要的两种操作次数作为坐标。

对障碍点按x排序一遍,令f[i]表示从原点到i号障碍点不经过任何障碍点的路径种数,那么得到DP方程:

f[i]=g(x[i],y[i])-Σf[j]*g(x[i]-x[j],y[i]-y[j]),j<i且y[j]<y[i]

其中g(i,j)表示从原点到(i,j)的方案数。

预处理出阶乘、逆元的阶乘就可以了。

时间复杂度O(n*logn)

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 #define N 510
 7 #define Max 1000000
 8 #define M 1000000007
 9 struct Node{
10     int x,y;
11 }a[N],A,B;
12 int i,j,k,n,m,x,y,f[N],J1[Max],J2[Max],Ni[Max],X,Y;
13 inline void Solve(int x,int y){
14     int a1=B.x*y-x*B.y,a2=A.y*B.x-A.x*B.y;
15     if(a1%a2)return;
16     int Ax=a1/a2;
17     a1=A.x*y-A.y*x,a2=A.x*B.y-A.y*B.x;
18     if(a1%a2)return;
19     int Ay=a1/a2;
20     if(Ax<0||Ay<0)return;
21     if(i<Max&&(Ax>a[1].x||Ay>a[1].y))return;
22     a[++m].x=Ax;a[m].y=Ay;
23 }
24 inline bool Cmp(Node a,Node b){
25     return a.x<b.x||(a.x==b.x&&a.y<b.y);
26 }
27 inline int Work(int x,int y){
28     return (1ll*J1[x+y]*J2[x])%M*J2[y]%M;
29 }
30 int main()
31 {
32     scanf("%d%d%d%d%d%d%d",&X,&Y,&n,&A.x,&A.y,&B.x,&B.y);
33     for(J1[1]=J2[1]=Ni[1]=J1[0]=J2[0]=1,i=2;i<Max;i++)J1[i]=1ll*J1[i-1]*i%M,Ni[i]=1ll*(M-M/i)*Ni[M%i]%M,J2[i]=1ll*J2[i-1]*Ni[i]%M;
34     Solve(X,Y);
35     if(m==0){printf("0");return 0;}
36     for(i=1;i<=n;i++){
37         scanf("%d%d",&x,&y);Solve(x,y);
38         if(x==X&&y==Y){printf("0");return 0;}
39     }
40     sort(a+1,a+m+1,Cmp);
41     for(i=1;i<=m;i++){
42         f[i]=Work(a[i].x,a[i].y);
43         for(j=1;j<i;j++)if(a[j].y<=a[i].y)f[i]=(f[i]-1ll*f[j]*Work(a[i].x-a[j].x,a[i].y-a[j].y)%M)%M;
44     }
45     printf("%d",(f[m]+M)%M);
46     return 0;
47 }
bzoj4767


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值