• APIO?~2019


    快要APIO了。切切往年的APIO,如何呢?

    APIO2019

    T1 - 奇怪装置

    这应该是个人均会的题目吧(

    好的,现在看来我应该不至于爆零了(

    洛谷题目页面传送门

    给定(n,a,b)以及(n)个区间([l_i,r_i]),求(left|igcuplimits_{i=1}^nleft{left(left(x+leftlfloordfrac xb ight floor ight)mod a,xmod b ight)mid xin[l_i,r_i] ight} ight|)

    (ninleft[1,10^6 ight],a,binleft[1,10^{18} ight],0leq l_ileq r_ileq 10^{18})

    首先可以手玩或者写个程序打个表,摸清楚(left(left(x+leftlfloordfrac xb ight floor ight)mod a,xmod b ight))这个东西的规律。不难发现,随着(x)加一,第二个数肯定是加一(在(mod b)意义下),第一个数先加上个一,然后如果(bmid x)就再加个一(在(mod a)意义下)。

    由于每步的二元组值只跟上一步有关,而且值域有限大,所以显然是有循环节的。不妨找出这个循环节。

    显然,第二个满足二元组等于((0,0))(x)就是循环节大小(第一个(x=0))。注意到,第一个数的增量与第二个数有关,而第二个数自己加自己的不受干扰,所以设(x=yb),其中(y)是整数。显然,在从(0)开始的前(x)次增加中,第一个数正常加的是(x)次,额外增加了(y)次。在(mod a)意义下为(0),所以满足(amid x+y),即(amid y(b+1))。即(dfrac{a}{gcd(a,b+1)}mid ydfrac{b+1}{gcd(a,b+1)}),显然(y=dfrac{a}{gcd(a,b+1)})时取得最小值,于是循环节为(x=yb=dfrac{a}{gcd(a,b+1)}b)

    接下来,把每个区间(l,r)都弄成(mod x)意义下的集合,分三种情况:

    1. (r-l+1leq x),此时显然它一个人就承包了值域里的所有元素,直接输出(x)走人;
    2. 否则,(lmod x>rmod x),显然是跨过了一个断点,可以将它拆成([lmod x,x-1],[0,rmod x])两个区间;
    3. 否则,(lmod xleq rmod x),就变成([lmod x,rmod x])这一个区间。

    接下来就是个区间并的事了。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    #define pb push_back
    #define ppb pop_back
    #define mp make_pair
    #define X first
    #define Y second
    const int inf=0x3f3f3f3f3f3f3f3f;
    int gcd(int x,int y){return y?gcd(y,x%y):x;}
    int n,a,b;
    vector<pair<int,int> > rg,ans;
    signed main(){
    	cin>>n>>a>>b;
    	int x=a/gcd(a,b+1);
    	x=x<LLONG_MAX/b?x*b:inf;
    	while(n--){
    		int l,r;
    		scanf("%lld%lld",&l,&r);
    		if(r-l+1>=x)return cout<<x,0;
    		else if(l%x>r%x)rg.pb(mp(l%x,x-1)),rg.pb(mp(0,r%x));
    		else rg.pb(mp(l%x,r%x));//分三种情况 
    	}
    	sort(rg.begin(),rg.end());
    	for(int i=0;i<rg.size();i++)//区间并 
    		if(ans.size()&&rg[i].X<=ans.back().Y)ans.back().Y=max(ans.back().Y,rg[i].Y);
    		else ans.pb(rg[i]);
    //	for(int i=0;i<ans.size();i++)printf("[%lld,%lld]
    ",ans[i].X,ans[i].Y);
    	int ans0=0;
    	for(int i=0;i<ans.size();i++)ans0+=ans[i].Y-ans[i].X+1;//统计答案 
    	cout<<ans0;
    	return 0;
    }
    

    T3 - 路灯

    洛谷题目页面传送门

    题意见洛谷。

    首先考虑实时维护(forall i,jin[1,n]),目前有多少时刻满足能从(i)到达(j)

    显然,(i)能到达(j)当且仅当(i,j)在同一个亮灯连续段里。将当前状态剖成若干个极大亮灯连续段,那么能否到达关于(i,j)两维的函数应该是由这些连续段,每段分别在(i)轴和(j)轴上作为两条邻边、(j=i)上的一段作为副对角线的一些充满(1)的正方形组成的,其他地方都是(0)(别问为啥不贴图,问就是我不是良心博主)

    那么在某一时刻的答案函数就是之前所有时刻的所对应的上述01函数之和。考虑实时维护这个答案函数(二维函数,即一个矩阵)。

    一种很容易想到的方法是递推,每次将当前时刻的01函数加到当前维护的答案函数里面去。当前时刻的01函数显然是由上一时刻的01函数进行常数次矩形修改得来的(开灯就是连接两边接壤的(1)矩形并补全,关灯就是从所在(1)矩形断开,至于如何维护这些连续段,set即可,太简单不多说),而将01函数加到答案函数里去又等价于对答案矩阵进行若干次(这里不是常数次了,而是连续段个数次,即(1)矩形个数次)矩形增加(1)。这里对01函数的更新显然是力所能及的,而对答案函数的更新的复杂度就没法保证了。

    不难发现,这里对01函数的更新是若干次矩形加(1),其中(1)是个常数,而这些矩形随时刻递增又是常数差异的,就一脸可以优化成在这些常数级别的差异上增加非常数,从而保证复杂度。

    考虑一个01函数关于时刻的三维函数。考虑当前答案函数的每一处在这个三维函数上的意义:显然是从当前时刻断开时间轴得到的纵切面的左边的所有此处的和。这是一些(0/1)的和,(0)显然不用考虑,剩下来就是一些(1)的区间,设为([l_1,r_1],[l_2,r_2],cdots,[l_k,r_k]),那么这个和就是(sumlimits_{i=1}^k(r_i-l_i+1))。考虑在一个区间开始的时候给此处贡献上(-l_i),在区间结束的时候给此处贡献上(r_i+1)。此时增加量显然不是常数了,我们来看看增加次数有没有减少。区间开始和区间结束的时候,就是相邻01函数差异对此处影响的时候,每次时刻的递推都只有01函数差异的矩形量次矩形增加,哦吼,可以了。需要注意的是,若当前要查询的那处在01函数中为(1)的话,那么第(k)个区间还未结束,我们要强行令它结束,即在答案函数在此处的值的基础上再加上当前时刻。

    (上面这一段重要的转化是本题的瓶颈。个人感觉这个哲学思想理解的还不是很透彻,大概以后重点做DS的时候题做多了感觉就上来了吧。)

    接下来就是个矩形增加、单点查询的事了。看起来能够线段树套动态开点线段树,但写到一半才发现外层线段树的懒标记无法(mathrm O(1))存储。于是想到将修改和查询范围颠倒的方式:差分。考虑二维差分,这样一次矩形增加转化为(4)次单点增加,单点查询转化为前缀矩形求和。这就是个经典的二维数点模型,写一发BIT套动态开点线段树即可(萌新第一次写这个,多多关照)。二维数点应该是有其他方法的(如cdq分治),然而我还没有系统的学这一块,不管,而且这题重点不在这里。

    btw,这是我第二次写vector动态开点数据结构(第一次是列队),犯了跟列队同样的错误(UB+vector分配内存原理造成赋不进去值)。多错几次就不会犯了。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    #define X first
    #define Y second
    const int inf=0x3f3f3f3f;
    int lowbit(int x){return x&-x;}
    const int N=300000;
    int n,qu; 
    char a[N+5];
    struct segtree{//动态开点线段树 
    	struct node{int lson,rson,l,r,sum;};
    	#define lson(p) nd[p].lson
    	#define rson(p) nd[p].rson
    	#define l(p) nd[p].l
    	#define r(p) nd[p].r
    	#define sum(p) nd[p].sum
    	vector<node> nd;
    	int nwnd(int l=1,int r=n){return nd.pb(node({0,0,l,r,0})),nd.size()-1;}
    	void init(){//初始化 
    		nd.pb(node({0,0,0,0,0}));
    		nwnd();
    	}
    	void add(int x,int v,int p=1){//单点增加 
    		sum(p)+=v;
    		if(l(p)==r(p))return;
    		int mid=l(p)+r(p)>>1,res;
    		if(x<=mid){
    			if(!lson(p))res=nwnd(l(p),mid),lson(p)=res;
    			add(x,v,lson(p));
    		}
    		else{
    			if(!rson(p))res=nwnd(mid+1,r(p)),rson(p)=res;
    			add(x,v,rson(p));
    		}
    	}
    	int _sum(int l,int r,int p=1){//区间求和 
    		if(!p)return 0; 
    		if(l<=l(p)&&r>=r(p))return sum(p);
    		int mid=l(p)+r(p)>>1,res=0;
    		if(l<=mid)res+=_sum(l,r,lson(p));
    		if(r>mid)res+=_sum(l,r,rson(p));
    		return res;
    	}
    };
    struct bitree{//BIT 
    	segtree segt[N+1];
    	void init(){//初始化 
    		for(int i=1;i<=n;i++)segt[i].init();
    	}
    	void add(int x,int y,int v){
    		if(y>n)return;
    		while(x<=n)segt[x].add(y,v),x+=lowbit(x);
    	}
    	void add(int l1,int r1,int l2,int r2,int v){//矩形增加 
    		add(r1+1,r2+1,v);add(l1,r2+1,-v);add(r1+1,l2,-v);add(l1,l2,v);//转化为差分数组上的单点增加 
    	}
    	int val(int x,int y){//单点查询 
    		int res=0;
    		while(x)res+=segt[x]._sum(1,y),x-=lowbit(x);//转化为差分数组上的矩形求和 
    		return res;
    	}
    }bit;
    int main(){
    	cin>>n>>qu;
    	scanf("%s",a+1);
    	set<pair<int,int> > st;
    	for(int i=1,las=0;i<=n+1;i++)
    		if(a[i]=='1')las=las?las:i;
    		else if(las)st.insert(mp(las,i-1)),las=0;
    	bit.init();
    	for(int i=1;i<=qu;i++){
    		char tp[10];int x,y;
    		scanf(" %s%d",tp,&x);
    		if(tp[0]=='t'){
    			set<pair<int,int> >::iterator fd=st.upper_bound(mp(x,inf));
    			if(fd!=st.begin()&&x<=(--fd)->Y){//关灯 
    				int l=fd->X,r=fd->Y;
    				st.erase(fd);
    				bit.add(l,r,l,r,i);
    				if(l<x)st.insert(mp(l,x-1)),bit.add(l,x-1,l,x-1,-i);
    				if(r>x)st.insert(mp(x+1,r)),bit.add(x+1,r,x+1,r,-i);
    			}
    			else{//开灯 
    				fd=st.upper_bound(mp(x,inf));
    				int l=x,r=x;
    				if(fd!=st.begin())fd--,fd->Y==x-1?bit.add(fd->X,fd->Y,fd->X,fd->Y,i),l=fd->X,st.erase(fd++):fd++;
    				if(fd!=st.end())fd->X==x+1&&(bit.add(fd->X,fd->Y,fd->X,fd->Y,i),r=fd->Y,st.erase(fd),0);
    				st.insert(mp(l,r));
    				bit.add(l,r,l,r,-i);
    			}
    		}
    		else{
    			scanf("%d",&y);y--;
    			set<pair<int,int> >::iterator fd=st.upper_bound(mp(x,inf));
    			if(fd!=st.begin()&&y<=(--fd)->Y)printf("%d
    ",bit.val(x,y)+i);
    			else printf("%d
    ",bit.val(x,y));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    【python+selenium】selenium grid(分布式)
    【python】导入自定义模块
    Maven的配置以及IDEA导入本地Maven
    java历史概述
    JVM 内存调优 与 实际案例
    ConcurrentHashMap实现线程安全的原理
    Request.UrlReferrer详解
    等比例缩放生成缩略图
    JavaEE的ajax入门
    javaee三层架构案例--简单学生管理系统
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/APIO-wtf-2019.html
Copyright © 2020-2023  润新知