修仙录 3.26


忘记清空邻接表了。
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(1xai)
这下式子是变得美丽了。。。
但是还是不好动
取对数,化乘为加!!!
e p = Π i = l r ( 1 − a i x ) e^p=\Pi_{i=l}^r(1-\frac{a_i}{x}) ep=Πi=lr(1xai)
∴ p = ∑ i = l r ln ⁡ ( 1 − a i x ) \therefore p=\sum_{i=l}^r\ln(1-\frac{a_i}{x}) p=i=lrln(1xai)
然后呢?
泰勒展开!!!(鬼知道是什么东西)
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!Πai+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)i1ixi
− 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(1x)=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=lri=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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值