寒假训练1.17

滚动莫队的题
洛谷AT1219
acwing 2153

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+40;
vector<int>s;
int a[N],col[N];
int find(int x)
{
	return (lower_bound(s.begin(),s.end(),x)-s.begin());
} 
int b[N];//还有还需要回来
int n,m;
ll ans[N],num[N];
int clear[N];
struct node{
	int l,r,i;
	bool operator<(const node&a)const {
		return (b[l]!=b[a.l])?(b[l]<b[a.l]):(r<a.r);
	}
}q[N];
ll cal(int l,int r)
{
	int last[N];
	ll res=0;
	for(int i=l;i<=r;i++)	last[col[i]]=0;
	for(int i=l;i<=r;i++){
		last[col[i]]++;
		res=max(res,last[col[i]]*(ll)s[col[i]]);
	}
	return res;
}


int main(){
	while(~scanf("%d%d",&n,&m))
	{
		s.clear();
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			s.push_back(a[i]);
		}	
		sort(s.begin(),s.end());
		s.erase(unique(s.begin(),s.end()),s.end());
		for(int i=1;i<=n;i++){
			col[i]=find(a[i]);
		}//直接做好了
		int len=sqrt(n);
		for(int i=0;i<m;i++)
		{
			scanf("%d%d",&q[i].l,&q[i].r);
			q[i].i=i;
		}
		for(int i=1;i<=n;i++){
			b[i]=(i-1)/len+1;
		}
		int bn=b[n];
		sort(q,q+m);
		for(int i=0,j=1;j<=bn;j++)
		{
			int br=min(n,j*len),l=br+1,r=br;
			ll res=0;
			int cs=0;
			for(;b[q[i].l]==j;i++)//开始进行一个枚举了 
			{
				if(b[q[i].r]==j)
				{
					ans[q[i].i]=cal(q[i].l,q[i].r);
					continue;
				}//然后就没了啊
				while(r<q[i].r)
				{
					r++;
					if(!num[col[r]]){
						clear[cs++]=col[r];
					}
					num[col[r]]++;//记得啊
					res=max(res,num[col[r]]*s[col[r]]);
				}
				ll te=res;//记录出来
				while(l>q[i].l)
				{
					--l;
					num[col[l]]++;
					res=max(res,num[col[l]]*s[col[l]]);	
				} 
				ans[q[i].i]=res;
				res=te;
				while(l<=br)
				{
					num[col[l++]]--;
				}
			}
			for(int i=0;i<cs;i++)
			{
				num[clear[i]]=0;
			}
		}
		for(int i=0;i<m;i++)
		{
			printf("%lld\n",ans[i]);
		}
	}
	return 0;
} 

今天午睡之后闹钟没叫醒直接睡到了晚上,今晚努力多打几题
欧拉序列+树上莫队
午睡之后调试的

//练习一下我的代码实现能力
//离散化,转化位欧拉序列
#include<bits/stdc++.h>
using namespace std;
const int N=8e4+50,M=1e5+40;
int num[N];
vector<int>s;
int a[N],col[N],d[N];
int find(int x){
    return (lower_bound(s.begin(),s.end(),x)-s.begin())+1;
}

int n,m;
int h[N],e[M],ne[M],idx,b[N],p[N];
int fir[N],sec[N],ts,f[N][25],cnt[N];
void add1(int u,int v)
{
    e[idx]=v,ne[idx]=h[u],h[u]=idx++;
}

void dfs(int u,int fa){
    fir[u]=++ts;
    d[u]=d[fa]+1;
    f[u][0]=fa;
    for(int i=1;i<=20;i++)//记得从1开始进行啊
    {
        f[u][i]=f[f[u][i-1]][i-1];
    }
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==fa)   continue;
        dfs(v,u);
    }
    sec[u]=++ts;
}//求解出欧拉的序列

int lca(int u,int v)
{
    if(d[u]>d[v]){
        swap(u,v);
    }
    for(int i=20;i>=0;i--)
    {
        if(d[f[v][i]]>=d[u]) v=f[v][i];
    }
    if(u==v)    return u;
    for(int i=20;i>=0;i--)
    {
        if(f[u][i]!=f[v][i]){
            u=f[u][i],v=f[v][i];
        }
    }
    return f[u][0];
}

struct node{
    int l,r,fa,i;
    bool operator<(const node& a)const{
        return (b[l]!=b[a.l])?(b[l]<b[a.l]):(r<a.r);
    }
}q[M];
int ans[M];
int res=0;
void add(int x,int k){
    num[k]^=1;
    if(!num[k]){
        cnt[p[k]]--;
        if(!cnt[p[k]])    res--;
    }else {
        if(!cnt[p[k]])    res++;
        cnt[p[k]]++;
    }
}
int l=1,r=0;
// void show(){
//     cout<<l<<" "<<r<<" debug "<<res<<endl;
// }
int main(){
    memset(h,-1,sizeof h);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        s.push_back(a[i]);
    }
    sort(s.begin(),s.end());
    s.erase(unique(s.begin(),s.end()),s.end());
    for(int i=1;i<=n;i++){
        p[i]=find(a[i]);
    }    
    int len=sqrt(2*n);
    for(int i=1;i<=2*n;i++)
    {
        b[i]=(i-1)/len+1;
    }
    int u,v;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add1(u,v),add1(v,u);
    }
    dfs(1,0);//默认以1位根结点
    for(int i=1;i<=n;i++)
    {
        col[fir[i]]=col[sec[i]]=i;//离散化
    }//记得离散化之后的赋值啊    
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&u,&v);  
        if(fir[u]>fir[v])//这判断u是先进入的可能新
        {
            swap(u,v);//直接进行一个交换
        }
        int g=lca(u,v);
        if(g==u||g==v){
            //这个的话就是直接进入就好了不需要
            q[i]={fir[u],fir[v],0,i};
        }else {
            q[i]={sec[u],fir[v],fir[g],i};
        }
    }
    sort(q,q+m);
    // int l=1,r=0;
    for(int i=0;i<m;i++){
        int x=q[i].l,y=q[i].r;
        //不管删除还是添加的话都是直接add就好了啊
        while(x<l){
            --l;
            add(l,col[l]);
        }
        while(y>r){
            r++;
            add(r,col[r]);
        }
        while(x>l)
        {
            add(l,col[l]);
            ++l;
        }
        while(y<r)
        {
            add(r,col[r]);
            --r;
        }
        int g=q[i].fa;
        if(!g){
            ans[q[i].i]=res;
        }else {
            add(g,col[g]);
            ans[q[i].i]=res;
            add(g,col[g]);//消除她的影响
        }
    }
    for(int i=0;i<m;i++)
    {
        printf("%d\n",ans[i]);
    }
    return 0;
}

开始学习二次离线莫队
acwing2535
洛谷P4887

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+40,M=1e5+40;
int w[N],b[N];
ll g[N],ans[N];
ll f[N],res;
vector<int>s;
int n,m,k;
int get_count(int x){
    int res=0;
    for(int i=0;i<14;i++){
        if((x>>i)&1){
            res++;
        }
    }
    return res;
}
struct node{
    int l,r,i;
    ll res;//这个地方也是有可能会爆的啊
    bool operator<(const node& a)const {
        return (b[l]!=b[a.l])?b[l]<b[a.l]:r<a.r;
    }
}q[N];//莫队直接用
struct Range{
    int l,r,i,t;//还有一个是符号的问题
};
vector<Range>range[N];//扫描线
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=0;i<(1<<14);i++){
        if(get_count(i)==k){
            s.push_back(i);
        }
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&w[i]);
    }
    for(int i=1;i<=n;i++){
        for(auto y:s){
            g[w[i]^y]++;//计算出结果
        }
        f[i]=g[w[i+1]];//获取的答案
    }
    for(int i=0;i<m;i++){
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].i=i,q[i].res=0;//清零的初始操作
    }
    int len=sqrt(n);
    for(int i=1;i<=n;i++){
        b[i]=i/len;
    }
    sort(q,q+m);//直接进行排序
    int l=1,r=0;
    //f[i]记得是前缀和为s[i]^(i+1)的含义
    for(int i=0;i<m;i++){
        int x=q[i].l,y=q[i].r;//然后的话直接开始做计算
        res=0;//记得每一次是重新开始算的增加的量而不是保持原本的
        if(r<y){
            range[l-1].push_back({r+1,y,i,-1});//减去的
        }
        while(r<y){
            res+=f[r++];
        }//这个是右端点往外扩展的
        if(r>y){
            range[l-1].push_back({y+1,r,i,1});
        }
        while(r>y){
            res-=f[--r];
        }
        if(l>x){
            range[r].push_back({x,l-1,i,1});
        }
        while(l>x){
            res-=(f[l-2] + !k );
            --l;
        }
        if(l<x){
            range[r].push_back({l,x-1,i,-1});
        }
        while(l<x){
            res+=( f[l-1] + !k );
            l++;
        }
        q[i].res=res;
    }//求解完了答案之后的话
    memset(g,0,sizeof g);
    for(int i=1;i<=n;i++){
        for(auto y:s){
            g[w[i]^y]++;
        }//预处理
        for(auto a:range[i]){
            int l=a.l,r=a.r;
            for(int x=l;x<=r;x++){
                q[a.i].res+=g[w[x]]*a.t;
            }
        }
        
    }
    ll sum=0;
    //差分之后的话还需要累加上去求解出他的前缀和
    for(int i=0;i<m;i++){
        sum+=q[i].res;
        ans[q[i].i]=sum;
    }
    for(int i=0;i<m;i++){
        printf("%lld\n",ans[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值