2021-10-19

ccpc题目
L题的分组背包
最小公倍数最大,并且每个数总和小于等于一个固定的数
因为最小公倍数看的是质因子
对此可以把每一个质数因子的p次方作为一个组
每个组内只能选取一个或者是两个

树形dp
求解最短路求出公式dis(a,b)+dis(a,c)+dis(b,c)
固定a,b的时候有c个可能所以就是对于每一个
dis(a,b)*cntc+dis(a,c)cntb+dis(b,c)cnta
sum/(cnta
cntb
cntc)
需要开__int128,long double
然后对于这个的话是做两边树形dp
求出所有的a的点到x,b的话到x,c的点到x的距离每个点,然后再遍历的过程之中判断x是哪一个类型这样话那么就能够求出答案了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+7;
int h[N],e[2*N],ne[2*N],w[N*2];
int idx,cnt;
void add(int u,int v,int c)
{
	e[idx]=v,ne[idx]=h[u],w[idx]=c,h[u]=idx++;
}
ll sa[N],sb[N],sc[N];
ll f[N][3];
ll dp[N][3];
int visa[N],visb[N],visc[N];
__int128 resa,resb,resc;
void dfs(int u,int fa)
{
	if(visa[u])
	{
		sa[u]=1;
	}
	if(visb[u])
	{
		sb[u]=1;
	}
	if(visc[u])
	{
		sc[u]=1;
	}
	for(int i=h[u];~i;i=ne[i])
	{
		int v=e[i];
		if(v==fa)	continue;
		dfs(v,u);
		sa[u]+=sa[v];
		sb[u]+=sb[v],sc[u]+=sc[v];
		dp[u][0]+=dp[v][0]+sa[v]*w[i];
		dp[u][1]+=dp[v][1]+sb[v]*w[i];
		dp[u][2]+=dp[v][2]+sc[v]*w[i];
	}
}

ll ma,mb,mc;

void dfs2(int u,int fa)
{
	if(visa[u])
	{		
		resa+=f[u][1]*mc;//ab
		resb+=f[u][2]*mb;//ac
	}
	if(visb[u])
	{
		resc+=f[u][2]*ma;
	}
	for(int i=h[u];~i;i=ne[i])
	{
		int v=e[i];
		if(v==fa)	continue;		
		int sza=ma-2*sa[v],szb=mb-2*sb[v],szc=mc-2*sc[v];
		ll ansa=f[u][0]-dp[v][0],ansb=f[u][1]-dp[v][1],ansc=f[u][2]-dp[v][2];
		f[v][0]=dp[v][0]+ansa+w[i]*sza;
		f[v][1]=dp[v][1]+ansb+w[i]*szb;
		f[v][2]=dp[v][2]+ansc+w[i]*szc;
		dfs2(v,u);
	}
}

int main(){
	memset(h,-1,sizeof h);
	int n;
	scanf("%d",&n);
	int u,v,c;
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%d",&u,&v,&c);
		add(u,v,c),add(v,u,c);
	}
	scanf("%d",&ma);
	int x;
	for(int i=1;i<=ma;i++)
	{
		scanf("%d",&x);
		visa[x]=1;
	} 
	scanf("%d",&mb);
	for(int i=1;i<=mb;i++)
	{
		scanf("%d",&x);
		visb[x]=1; 
	}
	scanf("%d",&mc);
	for(int i=1;i<=mc;i++)
	{
		scanf("%d",&x);
		visc[x]=1;
	}
	dfs(1,1);
	for(int i=0;i<=2;i++)
	{
		f[1][i]=dp[1][i];
	}
	dfs2(1,1); 
	ll sum=ma*mb*mc; 
	double res=(resa+resb+resc)*1.0/2/sum;
	printf("%.10f\n",res);
	return  0;
}

G题
有两个操作,其中一个是区间加上1的操作
然后的话还有一个是问的一个区间内再mod 65536下是不是完全相同的
题解的做法是线段数维护hash值
因为有区间的加法
然后考虑差分
然后如果比较初始点相同的话,并且两个差分的区间也是相同的话那么就是相同的
lazy数组求解的是点的修改的大小
用于判断区间的头是否是相同的
然后是hash维护的

//线段树维护hash的值 
#include <bits/stdc++.h>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define _CRT_SECURE_NO_WARNINGS
typedef  long long ll;
using namespace std;
#define rep(i,j,n) for(ll i=j;i<=n;i++)
typedef unsigned long long ull;
typedef unsigned short us;
const ll INF= 1e16;
const ll maxn = 3e6+7;
const ll mod = 1e9+7;
const ll up = 1e13;
const ll base= 10;
inline bool read(ll &num)
{char in;bool IsN=false;
    in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
ll sum[maxn],num[maxn],qb[maxn];
ll lazy[maxn];
void push(int k,int l,int r){
    int mid = (l+r)/2;
    sum[k] = (sum[k<<1]*qb[r-mid] + sum[k<<1|1])%mod;
}
void down(int k){
    if(lazy[k]){
        lazy[k<<1] += lazy[k];
        lazy[k<<1|1] += lazy[k];
        lazy[k] = 0;
    }
}
void Modify(int k,int l,int r,int x,int y){
    if(x<=l&&y>=r){
        lazy[k]++;
        return;
    }
    down(k);
    int mid = (l+r)/2;
    if(x<=mid) Modify(k<<1,l,mid,x,y);
    if(y>mid) Modify(k<<1|1,mid+1,r,x,y);
}
ll Find(int k,int l,int r,int pos){
    if(l == r) return lazy[k];
    int mid = (l+r)/2;
    down(k);
    if(pos<=mid) return Find(k<<1,l,mid,pos);
    return Find(k<<1|1,mid+1,r,pos);
}
void build(int k,int l,int r){
    if(l == r){
        sum[k] = (num[l]-num[l-1]%65536+65536)%65536;
        return;
    }
    int mid = (l+r)/2;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    push(k,l,r);
}
void ModifyOne(int k,int l,int r,int pos,int w){
    if(l == r){
        sum[k] = (sum[k] + w+65536)%65536;
        return ;
    }
    int mid = (l+r)/2;
    if(pos<=mid) ModifyOne(k<<1,l,mid,pos,w);
    else ModifyOne(k<<1|1,mid+1,r,pos,w);
    push(k,l,r);
}
ll Query(int k,int l,int r,int x,int y){
    if(x<=l&&y>=r) return (sum[k]*qb[y-r])%mod;
    int mid = (l+r)/2;
    ll ans = 0;
    if(x<=mid) ans += Query(k<<1,l,mid,x,y);
    if(y>mid) ans += Query(k<<1|1,mid+1,r,x,y);
    return ans%mod;
}
int main(){
 
    read(n);read(m);
    qb[0] = 1;
    for(int i=1;i<=n;i++) qb[i] = (qb[i-1]*base)%mod;
    for(int i=1;i<=n;i++) read(num[i]);
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int op,x,y,z;
        scanf("%d",&op);
        if(op == 1){
            scanf("%d%d",&x,&y);
            ModifyOne(1,1,n,x,1);
            if(y+1<=n) ModifyOne(1,1,n,y+1,-1);
            Modify(1,1,n,x,y);
        }else{
            scanf("%d%d%d",&x,&y,&z);
            if((num[x]+Find(1,1,n,x))%65536 == (num[y]+Find(1,1,n,y))%65536){
                ll tempx = 0;
                if(x+1<=x+z-1) tempx = Query(1,1,n,x+1,x+z-1);
                ll tempy = 0;
                if(y+1<=y+z-1) tempy = Query(1,1,n,y+1,y+z-1);
                printf("%s\n",tempx == tempy?"yes":"no");
            }
            else printf("no\n");
        }
    }
    return 0;
}
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值