4.13 BJ集训
T1 Mobitel
题目大意:
一个全是正整数的矩阵,求从左上角到右下角的简单路径有多少条路径上数的乘积$>=K$
思路:
由于整数分块,我们设$f(i,j,k)$表示走到$(i,j)$,$k=K/$(路径上数的乘积),的方案数
然后转移还是正常转移,需要注意把$k--$,因为只能求$>k-1$
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstring> 4 #include<cstdio> 5 #include<algorithm> 6 #include<cmath> 7 #include<vector> 8 #include<queue> 9 #define rep(i,s,t) for(register int i=(s),i##end=(t);i<=i##end;++i) 10 #define dwn(i,s,t) for(register int i=(s),i##end=(t);i>=i##end;--i) 11 #define ren for(int i=fst[x];i;i=nxt[i]) 12 #define ll long long 13 #define MAXN 305 14 #define MOD 1000000007 15 #define pls(a,b) ((a)+(b))%MOD 16 #define mul(a,b) (1LL*(a)*(b))%MOD 17 using namespace std; 18 inline int read() 19 { 20 int x=0,f=1;char ch=getchar(); 21 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 22 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 23 return x*f; 24 } 25 int n,m,g[MAXN][MAXN],val[3050],id[1001000];int k; 26 int f[2][MAXN][MAXN*10],tot; 27 void inc(int &x,int y) {x= (x+y>=MOD)?x+y-MOD:x+y;} 28 int main() 29 { 30 freopen("mobitel.in","r",stdin);freopen("mobitel.out","w",stdout); 31 n=read(),m=read(),k=read()-1;rep(i,1,n) rep(j,1,m) g[i][j]=read();int c,pos; 32 rep(i,1,k) pos=k/(k/i),val[++tot]=k/i,i=pos;++tot;rep(i,1,tot) id[val[i]]=i; 33 f[1][1][id[k/g[1][1]]]=1;rep(t,1,n) 34 { 35 c=t&1;rep(i,1,m) 36 rep(j,1,tot) {inc(f[c][i][id[val[j]/g[t][i]]],f[!c][i][j]); 37 if(i<m) inc(f[c][i+1][id[val[j]/g[t][i+1]]],f[c][i][j]);} 38 memset(f[!c],0,sizeof(f[!c])); 39 } 40 printf("%d\n",f[n&1][m][tot]); 41 }
T2 transport
题目大意:
一个树上,每个点有权值,边权有权值
求有多少对点对满足对于这条路径任意一个前缀都满足点的权值和>边的权值和
思路:
很明显的点分治,对每个分治重心
搜出每一条从重心开始的链需要之前盈余多少权值才能走到,搜出每一条能走到重心的链到根后盈余多少
(第一个分别记录$dis$的最低值,第二个记录最小的一个后缀判断能否走到
排序后双指针,然后容斥一下即可
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstring> 4 #include<cstdio> 5 #include<algorithm> 6 #include<cmath> 7 #include<vector> 8 #include<queue> 9 #define rep(i,s,t) for(register int i=(s),i##end=(t);i<=i##end;++i) 10 #define dwn(i,s,t) for(register int i=(s),i##end=(t);i>=i##end;--i) 11 #define ren for(int i=fst[x];i;i=nxt[i]) 12 #define ll long long 13 #define inf 2139062143 14 #define MAXN 100100 15 #define MOD 1000000007 16 #define pls(a,b) ((a)+(b))%MOD 17 #define mul(a,b) (1LL*(a)*(b))%MOD 18 using namespace std; 19 inline int read() 20 { 21 int x=0,f=1;char ch=getchar(); 22 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 23 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 24 return x*f; 25 } 26 int n,m,v[MAXN],fst[MAXN],nxt[MAXN<<1],to[MAXN<<1],val[MAXN<<1]; 27 int mx[MAXN],sz[MAXN],Sum,rt,Mx,vis[MAXN],cnt; 28 void add(int u,int v,int w) {nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v,val[cnt]=w;} 29 void getrt(int x,int pa) 30 { 31 mx[x]=0,sz[x]=1;ren if(to[i]^pa&&!vis[to[i]]) 32 getrt(to[i],x),sz[x]+=sz[to[i]],mx[x]=max(mx[x],sz[to[i]]); 33 mx[x]=max(mx[x],Sum-sz[x]);if(mx[x]<Mx) Mx=mx[x],rt=x; 34 } 35 ll g[MAXN],f[MAXN],ans;int ln,len; 36 void get1(int x,int pa,ll mn,ll dis) 37 { 38 g[++len]=mn,dis+=v[x];ren if(to[i]^pa&&!vis[to[i]]) 39 get1(to[i],x,min(dis-val[i],mn),dis-val[i]); 40 } 41 void get2(int x,int pa,ll mx,ll dis) 42 { 43 if(v[x]-mx>=0) f[++ln]=v[x]+dis;mx-=v[x],dis+=v[x];ren if(to[i]^pa&&!vis[to[i]]) 44 get2(to[i],x,max((ll)val[i],mx+val[i]),dis-val[i]); 45 } 46 void calc(int x,int w,ll res=0) 47 { 48 int tmp=0,pos=ln;sort(g+1,g+len+1);sort(f+1,f+ln+1); 49 rep(i,1,len) {while(pos&&g[i]+f[pos]>=0) tmp++,pos--;res+=tmp;}ans+=res*w; 50 } 51 void div(int x) 52 { 53 vis[x]=1;ln=len=0;get1(x,0,0,0);get2(x,0,0,-v[x]); 54 calc(x,1);ans--;ren if(!vis[to[i]]) 55 { 56 ln=len=0;get1(to[i],x,v[x]-val[i],v[x]-val[i]); 57 get2(to[i],x,val[i],-val[i]);calc(to[i],-1); 58 } 59 ren if(!vis[to[i]]) {Sum=sz[to[i]],Mx=n+1;getrt(to[i],x);div(rt);} 60 } 61 int main() 62 { 63 freopen("transport.in","r",stdin);freopen("transport.out","w",stdout); 64 n=read();int a,b,c;rep(i,1,n) v[i]=read(); 65 rep(i,2,n) a=read(),b=read(),c=read(),add(a,b,c),add(b,a,c); 66 Sum=n,Mx=n+1;getrt(1,0);div(rt);printf("%lld\n",ans); 67 }
T3
题目大意:
$n$个1,要分成$k$段,每一段$[l,r]$的贡献为$\frac{r-l+1}{n-l+1}$,求最大贡献
思路:
这种恰好$k$段的可以二分
每一次分段都加上一个二分出来的这个值,判断最优的段数与$k$的关系来二分
转移的方程是$f_i=f_j+\frac{i-j}{n-j}+mid$ 是一个斜率优化的式子
然后就结束了
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstring> 4 #include<cstdio> 5 #include<algorithm> 6 #include<cmath> 7 #include<vector> 8 #include<queue> 9 #define rep(i,s,t) for(register int i=(s),i##end=(t);i<=i##end;++i) 10 #define dwn(i,s,t) for(register int i=(s),i##end=(t);i>=i##end;--i) 11 #define ren for(int i=fst[x];i;i=nxt[i]) 12 #define ll long long 13 #define db double 14 #define MAXN 100100 15 #define pls(a,b) ((a)+(b))%MOD 16 #define mul(a,b) (1LL*(a)*(b))%MOD 17 using namespace std; 18 inline int read() 19 { 20 int x=0,f=1;char ch=getchar(); 21 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 22 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 23 return x*f; 24 } 25 int n,k,pre[MAXN],nxt[MAXN],tm[MAXN],tl,hd,q[MAXN];db f[MAXN]; 26 db Y(int i,int j) {return f[i]-f[j]-1.0*i/(n-i)+1.0*j/(n-j);} 27 db X(int i,int j) {return 1.0/(n-i)-1.0/(n-j);} 28 int cheq(db x) 29 { 30 memset(f,0,sizeof(f));q[hd=tl=1]=0;int t;rep(i,1,n) 31 { 32 while(hd<tl&&Y(q[hd],q[hd+1])<=-i*X(q[hd],q[hd+1])) hd++; 33 t=pre[i]=q[hd],f[i]=f[t]+1.0*(i-t)/(n-t)-x,tm[i]=tm[t]+1,pre[i]=t; 34 while(hd<tl&&Y(q[tl-1],q[tl])*X(q[tl],i)<Y(q[tl],i)*X(q[tl-1],q[tl])) tl--; 35 q[++tl]=i; 36 }return tm[n]; 37 } 38 int main() 39 { 40 freopen("quiz.in","r",stdin);freopen("quiz.out","w",stdout); 41 n=read(),k=read();db l=0,r=1,mid; 42 for(int t=0,tmp;mid=(l+r)/2.0,t<=100;t++) 43 { 44 if((tmp=cheq(mid))>k) l=mid; 45 else if(tmp<k) r=mid;else break; 46 } 47 printf("%.9lf\n",f[n]+k*mid); 48 }