考
忘记清空邻接表了。
dinic那个优化好重要。
AC是不可能的。
jzoj 4528 要换换名字
https://jzoj.net/senior/#contest/show/2683/0
枚举必须选点i,并以i为根
那么要选另外一个点,就要选他到i(根)的所有点,其实选父亲就行了
如果把点向父亲连边,问题就转变成了最大权联通子图。
https://blog.csdn.net/can919/article/details/77603353
dinic一句话没写就是T。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN=105;
int op,n,a[MAXN],S,T,ans;
struct tree{
int cnt,head[MAXN],to[MAXN*2],next[MAXN*2];
void init(){
cnt=0,memset(head,0,sizeof(head));
}
void add(int u,int v){
next[++cnt]=head[u],to[cnt]=v,head[u]=cnt;
next[++cnt]=head[v],to[cnt]=u,head[v]=cnt;
}
}t1,t2;
int head[MAXN],next[MAXN*10],to[MAXN*10],w[MAXN*10],cnt;
void add(int u,int v,int l){
next[++cnt]=head[u],to[cnt]=v,w[cnt]=l,head[u]=cnt;
next[++cnt]=head[v],to[cnt]=u,w[cnt]=0,head[v]=cnt;
}
void dfs_1(int x,int F){
if(F) add(x,F,1e9);
for(int i=t1.head[x];i;i=t1.next[i]){
int y=t1.to[i];
if(y==F) continue;
dfs_1(y,x);
}
}
void dfs_2(int x,int F){
if(F) add(x,F,1e9);
for(int i=t2.head[x];i;i=t2.next[i]){
int y=t2.to[i];
if(y==F) continue;
dfs_2(y,x);
}
}
queue<int>q;
int d[MAXN];
bool bfs(){
memset(d,0,sizeof(d));
q.push(S),d[S]=1;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=next[i]){
int y=to[i];
if(w[i]>0&&!d[y]) d[y]=d[x]+1,q.push(y);
}
}
return d[T];
}
int dfs(int x,int now){
if(x==T) return now;
int flow=0;
for(int i=head[x];i;i=next[i]){
int y=to[i];
if(d[y]==d[x]+1&&w[i]>0){
int can=dfs(y,min(w[i],now));
now-=can,flow+=can;
w[i]-=can,w[i^1]+=can;
if(!now) break;
}
}
if(now) d[x]=-1;
return flow;
}
int dinic(){
int ans=0;
while(bfs()) ans+=dfs(S,1e9);
return ans;
}
int calc(int rt){
S=0,T=n+1,cnt=1,memset(head,0,sizeof(head));
int sum=0;
for(int i=1;i<=n;i++){
if(a[i]>0) add(S,i,a[i]),sum+=a[i];
else add(i,T,-a[i]);
}
dfs_1(rt,0),dfs_2(rt,0);
return sum-dinic();
}
int main(){
cin>>op;
while(op--){
scanf("%d",&n);t1.init(),t2.init();
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),t1.add(u,v);
for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),t2.add(u,v);
ans=0;
for(int i=1;i<=n;i++) ans=max(ans,calc(i));
printf("%d\n",ans);
}
return 0;
}
jzoj 6086 动态半平面交
https://jzoj.net/senior/#contest/show/2683/1
因为我们要求的是lcm
首先因为要取模,不能用乘积除以gcd来算。
但是我们还可以分解质因数呀
lcm其实就是所有数的每个质因数的最高次乘积
线性筛预处理prime,顺便记录每个数的最小质因子,这样分解的时候就不用枚举了
然后按照深度,每层都对dfn开线段树维护就好了
现在我们要处理一个问题:不同的数有相同的质因子怎么去重
这里用了一个神奇的方法。
把某个质因子p的每个次方看成有一种颜色(一个p相同次方为同色),并且贡献都为p
对于每个颜色可能出现在不同位置(质因子可能多个点都有),但是贡献不能算重,
只有两个点:
发现其实只要在lca处除一个p就好啦:这样最后查询的时候,这个子树的贡献就只有一个p了
如果再添加一个点:
按dfn先查询出改添加的位置,和左边的lca处除p,和右边的lca处除p,原来的lca处乘回来
这代码真不是人打得出来的
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#define LL long long
using namespace std;
const int MAXN=1e6+5;
const int MAXM=1e7+5;
const int mod=998244353;
int k,n,q,a[MAXN],ma,pos[MAXN];
int ans;
int p[MAXM],is[MAXM],sp,pm[MAXM],id[MAXM];
void prework(){
for(int i=2;i<=ma;i++){
if(!is[i]) p[++sp]=pm[i]=i,id[i]=sp;
for(int j=1;j<=sp&&i*p[j]<=ma;j++){
is[i*p[j]]=1,pm[i*p[j]]=p[j];
if(i%p[j]==0) break;
}
}
}
int head[MAXN],to[MAXN*2],next[MAXN*2],cnt;
void add(int u,int v){
next[++cnt]=head[u],to[cnt]=v,head[u]=cnt;
next[++cnt]=head[v],to[cnt]=u,head[v]=cnt;
}
int dfn[MAXN],las[MAXN],dep[MAXN],tot,dd[MAXN],fa[MAXN][20];
void dfs(int x,int F){
dfn[x]=++tot,dd[tot]=x,fa[x][0]=F,dep[x]=dep[F]+1;
for(int i=1;i<=18;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=head[x];i;i=next[i]){
int y=to[i];
if(y==F) continue;
dfs(y,x);
}
las[x]=tot;
}
int prime[MAXN],mi[MAXN],sum[MAXN];
int get_p(int x){
int last=0,sum=0;
while(x>1){
if(pm[x]!=last) last=prime[++sum]=pm[x],mi[sum]=0;
mi[sum]++;
x/=pm[x];
}
return sum;
}
bool comp(int x,int y){
return dep[x]<dep[y];
}
int rt[MAXN],lc[MAXN*4],rc[MAXN*4],bl[MAXN*4],tr[MAXN*4],nd;
void modify(int &p,int l,int r,int x,int v,int rt){
if(bl[p]!=rt) tr[++nd]=tr[p],lc[nd]=lc[p],rc[nd]=rc[p],bl[nd]=rt,p=nd;
tr[p]=1ll*tr[p]*v%mod;
if(l==r) return;
int mid=l+r>>1;
if(x<=mid) modify(lc[p],l,mid,x,v,rt);
else modify(rc[p],mid+1,r,x,v,rt);
}
int Pow(int a,int b){
int ans=1;
while(b){
if(b&1) ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ans;
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=18;i>=0;i--) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=18;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
set<int>s[MAXN];
void insert(int c,int x,int d,int p){
modify(rt[d],1,n,dfn[x],p,d);
int inv=Pow(p,mod-2);
if(s[c].size()){
set<int>::iterator it=s[c].lower_bound(dfn[x]);
if(it==s[c].begin()) modify(rt[d],1,n,dfn[lca(x,dd[*it])],inv,d);
else if(it==s[c].end()) modify(rt[d],1,n,dfn[lca(x,dd[*(--it)])],inv,d);
else{
int y=dd[*it],z=dd[*(--it)];
modify(rt[d],1,n,dfn[lca(x,y)],inv,d);
modify(rt[d],1,n,dfn[lca(x,z)],inv,d);
modify(rt[d],1,n,dfn[lca(y,z)],p,d);
}
}
s[c].insert(dfn[x]);
}
int ask(int p,int l,int r,int L,int R){
if(!p) return 1;
if(L<=l&&r<=R) return tr[p];
int mid=l+r>>1,ans=1;
if(L<=mid) ans=1ll*ans*ask(lc[p],l,mid,L,R)%mod;
if(mid<R) ans=1ll*ans*ask(rc[p],mid+1,r,L,R)%mod;
return ans;
}
int main(){
freopen("half.in","r",stdin);
freopen("half.out","w",stdout);
cin>>k>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),ma=max(ma,a[i]),pos[i]=i;
prework();
for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),add(u,v);
dfs(1,0);
for(int i=1;i<=n;i++){
int num=get_p(a[i]);
for(int j=1;j<=num;j++) sum[id[prime[j]]]=max(sum[id[prime[j]]],mi[j]);
}
for(int i=1;i<=sp;i++) sum[i]+=sum[i-1];
sort(pos+1,pos+1+n,comp);
tr[0]=1;
for(int l=1,r=0;l<=n;l=r+1){
while(r<n&&dep[pos[r+1]]==dep[pos[l]]) r++;
int d=dep[pos[l]];
rt[d]=rt[d-1];
for(int i=l;i<=r;i++){
int num=get_p(a[pos[i]]);
for(int j=1;j<=num;j++) for(int k=1;k<=mi[j];k++)
insert(sum[id[prime[j]]-1]+k,pos[i],d,prime[j]);
}
}
cin>>q;
while(q--){
int u,d;scanf("%d%d",&u,&d);
u^=k*ans,d^=k*ans;
ans=ask(rt[min(dep[pos[n]],dep[u]+d)],1,n,dfn[u],las[u]);
printf("%d\n",ans);
}
return 0;
}
jzoj 6087 获取名额
https://jzoj.net/senior/#main/show/6087
毒瘤数学题来啦。
首先,正向思考得出的式子不好动
于是反向思考:得到资格=1-没得到资格
所以我们要求的其实就是
1
−
Π
i
=
l
r
(
1
−
a
i
x
)
1-\Pi_{i=l}^r(1-\frac{a_i}{x})
1−Πi=lr(1−xai)
这下式子是变得美丽了。。。
但是还是不好动
取对数,化乘为加!!!
设
e
p
=
Π
i
=
l
r
(
1
−
a
i
x
)
e^p=\Pi_{i=l}^r(1-\frac{a_i}{x})
ep=Πi=lr(1−xai)
∴
p
=
∑
i
=
l
r
ln
(
1
−
a
i
x
)
\therefore p=\sum_{i=l}^r\ln(1-\frac{a_i}{x})
∴p=∑i=lrln(1−xai)
然后呢?
泰勒展开!!!(鬼知道是什么东西)
https://blog.csdn.net/qq_38906523/article/details/79851654
虽然可以背结论,还是小小的推一下好了,从简单入手
取
x
0
=
0
x_0=0
x0=0
∴
(
x
+
1
)
a
=
1
+
∑
i
=
1
n
Π
a
−
i
+
1
a
x
i
i
!
\therefore (x+1)^a=1+\sum_{i=1}^n\frac{\Pi_{a-i+1}^ax^i}{i!}
∴(x+1)a=1+∑i=1ni!Πa−i+1axi
另
a
=
−
1
a=-1
a=−1可得:
∴
1
x
+
1
=
1
+
∑
i
=
1
n
(
−
x
)
i
\therefore\frac{1}{x+1}=1+\sum_{i=1}^n(-x)^i
∴x+11=1+∑i=1n(−x)i
两边分别积分:
∴
ln
(
x
+
1
)
=
∑
i
=
1
n
(
−
1
)
i
−
1
x
i
i
\therefore\ln(x+1)=\sum_{i=1}^n(-1)^{i-1}\frac{x^i}{i}
∴ln(x+1)=∑i=1n(−1)i−1ixi
用
−
x
-x
−x代换
x
x
x
∴
ln
(
1
−
x
)
=
−
∑
i
=
1
n
x
i
i
\therefore\ln(1-x)=-\sum_{i=1}^n\frac{x^i}{i}
∴ln(1−x)=−∑i=1nixi
∴
p
=
−
∑
j
=
l
r
∑
i
=
1
n
(
a
j
)
i
i
x
i
\therefore p=-\sum_{j=l}^r\sum_{i=1}^n\frac{(a_j)^i}{ix^i}
∴p=−∑j=lr∑i=1nixi(aj)i
哎呀终于打完了
于是我们要处理的其实就是
(
a
j
x
)
i
(\frac{a_j}{x})^i
(xaj)i
分子前缀和预处理就好啦
但是!!
直接这么搞竟然会高精度
那么我们把分子分母都除max a
但是!!
直接这么搞竟然会有精度误差
减小精度误差我们就选一些点直接算,其他的再用前缀和一起算
选哪些点呢
可以发现当
a
i
x
\frac{a_i}{x}
xai比较大时对答案影响较大
那我们RMQ预处理最大值,把>0.5的值拿出来算就好
果真毒瘤
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=6e5+5;
int n,q,f[MAXN][25],lg[MAXN];
double a[MAXN],mx,s[MAXN][25],ml[MAXN],x;
double ans,sum;
int get_mx(int l,int r){
if(l==r) return l;
int len=lg[r-l+1];
return a[f[l][len]]>a[f[r-(1<<len)+1][len]] ? f[l][len] : f[r-(1<<len)+1][len];
}
void divide(int l,int r){
if(l>r) return ;
if(ans<1e-7) return ;
int p=get_mx(l,r);
if(a[p]*x>0.5){
for(int i=1;i<=20;i++) ml[i]-=s[p][i]-s[p-1][i];
ans*=1-a[p]*x;
divide(l,p-1),divide(p+1,r);
}
}
int main(){
freopen("orz.in","r",stdin);
freopen("orz.out","w",stdout);
cin>>n>>q;
for(int i=1;i<=n;i++) scanf("%lf",&a[i]),mx=max(mx,a[i]);
for(int i=1;i<=n;i++){
a[i]/=mx,f[i][0]=i;
double A=a[i];
for(int j=1;j<=20;j++,A*=a[i]) s[i][j]=s[i-1][j]-A/j;
}
for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
for(int j=1;j<=lg[n];j++) for(int i=1;i<=n-(1<<j)+1;i++)
f[i][j]= a[f[i][j-1]]>a[f[i+(1<<j-1)][j-1]] ? f[i][j-1] : f[i+(1<<j-1)][j-1];
while(q--){
int l,r;scanf("%d%d%lf",&l,&r,&x);
x/=mx,x=1.0/x;
ans=1,sum=0;
for(int i=1;i<=20;i++) ml[i]=s[r][i]-s[l-1][i];
divide(l,r);
double X=x;
for(int i=1;i<=20;i++,X*=x) sum+=ml[i]*X;
ans*=exp(sum);
printf("%.10lf\n",1.0-ans);
}
return 0;
}
我终于搞懂一道数学题啦QAQ