• cf contest 1458


    A.Row GCD

    (A,B)数组的长度分别为(n,m),对于(k=1 o n)(GCD(a_1+b_k,a_2+b_k,dots,a_n+b_k))

    (1le n,mle2cdot10^5),(1le a_i,b_ile10^18)

    solution

    注意到:(GCD(a_1+b_k,a_2+b_k,dots,a_n+b_k)=GCD(a_1+b_k,a_2-a_1,dots,a_n-a_{n-1}))

    预处理(GCD(a_1+b_k,a_2-a_1,dots,a_n-a_{n-1}))

    时间复杂度:(O((N+M)logA))

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N=200010;
    int n,m;
    ll a1,ga,ans;
    ll gcd(ll a,ll b){return !b?a:gcd(b,a%b);}
    int main(){
    	//freopen("a.in","r",stdin);
    	//freopen("a.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	scanf("%lld",&a1);
    	for(int i=2;i<=n;++i){
    		ll x;scanf("%lld",&x);
    		ga=gcd(abs(x-a1),ga);
    	}
    	for(int i=1;i<=m;++i){
    		ll x;scanf("%lld",&x);
    		ans=gcd(a1+x,ga);
    		printf("%lld ",ans);
    	}
    	return 0;
    }
    

    B. Glass Half Spilled

    (n)个杯子,每个杯子有容量(a_i)和水量(b_i)。一次操作为((i,j,x))表示把杯子(i)中的(x(xle b_i))倒入杯子(j)中,但是杯子(j)只能得到一半((frac{x}{2}))的水并且总水量不能超过(a_j) 。你可以进行任意操作,询问当最后可以剩下(k=1 o n)个杯子时,这(k)个杯子中的水量的和的最大值。

    (1le nle100,0le b_ile a_ile 100)

    solution

    显然不选的杯子的水一定会被倒完,并且不管分多少次倒都有一半会浪费。

    假设最后选的杯子集合为(S),(W)为所有(n)个杯子的水量之和,那么就是最大化$Min{sum_{iin S}a_i,frac{W}{2}+frac{1}{2}sum_{i in S}b_i } $

    枚举这两个式子的差值,只需要计算(Max { sum_{i in S} a_i })

    (f_{n,k,x})表示前(n)个杯子选(k)个差值为(x)的最大值,dp即可

    (M=2sum_{i=1}^{n} max { a_i,b_i })时间复杂度就是:(O(N^2M))

    #include<bits/stdc++.h>
    using namespace std;
    const int N=110,M=40010,inf=1e9;
    int n,a[N],b[N],mx,f[2][N][M],W,cur=0;
    void upd(int&x,int y){if(x<y)x=y;}
    int main(){
    	//freopen("b.in","r",stdin);
    	//freopen("b.out","w",stdout);
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i){
    		scanf("%d%d",&a[i],&b[i]);
    		mx+=max(2*a[i],b[i]);W+=b[i];
    	}
    	for(int i=0;i<=n;++i)
    	for(int j=-mx;j<=mx;++j)f[cur][i][j+mx]=-inf;
    	f[cur][0][0+mx]=0;
    	for(int i=1;i<=n;++i){
    		int t1=a[i],t2=b[i]-2*a[i];
    		for(int j=0;j<=n;++j)
    		for(int k=-mx;k<=mx;++k){
    			f[cur^1][j][k+mx]=f[cur][j][k+mx]; 
    		} 
    		for(int j=0;j<n;++j)
    		for(int k=-mx;k<=mx;++k){
    			if(k+mx<-mx||k+mx>mx)continue;
    			upd(f[cur^1][j+1][k+t2+mx],f[cur][j][k+mx]+t1);
    		}
    		cur^=1;
    	}
    	for(int i=1;i<=n;++i){
    		int ans=-inf;
    		for(int j=-mx;j<=mx;++j)
    		if(j+W>0)upd(ans,2*f[cur][i][j+mx]);
    		else upd(ans,2*f[cur][i][j+mx]+j+W);
    		
    		printf("%.10lf ",1.0*ans/2);
    	}	
    	return 0;
    }
    

    C. Latin Square

    给定一个(n imes n)的二维数组,满足每行每列都是一个排列。

    一次操作为:对所有行向右/左平移,对所有列向上/下平移,对所有行/列进行置换

    输出(m)次操作后最终的矩阵

    (1 le n le 10^3 \, 1 le m le 10^5)

    solution

    维护((x,y,z))表示$a[x][y]=z $

    时间复杂度:(O(n^2+m))

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1010,M=100010;
    int T,n,m,a[N][N],b[3],c[3],Ans[N][N];
    char s[M];
    void dec(int&x){if(--x<1)x+=n;}
    void inc(int&x){if(++x>n)x-=n;}
    int main(){
    	//freopen("C.in","r",stdin);
    	//freopen("C.out","w",stdout);
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d%d",&n,&m);
    		for(int i=1;i<=n;++i)
    		for(int j=1;j<=n;++j)scanf("%d",&a[i][j]);
    		scanf("%s",s+1);
    		for(int i=0;i<3;++i)b[i]=i,c[i]=0;
    		for(int i=1;i<=m;++i){
    			switch(s[i]){
    		 		case 'L':dec(c[b[1]]);break;
    				case 'R':inc(c[b[1]]);break;
    				case 'U':dec(c[b[0]]);break;
    				case 'D':inc(c[b[0]]);break;
    				case 'I':swap(b[1],b[2]);break;
    				case 'C':swap(b[0],b[2]);break; 
    			}
    		}
    		for(int i=1;i<=n;++i)
    		for(int j=1;j<=n;++j){
    			int x=(((!b[0])?i:(b[0]&1)?j:a[i][j])+c[b[0]]-1)%n+1;
    			int y=(((!b[1])?i:(b[1]&1)?j:a[i][j])+c[b[1]]-1)%n+1;
    			int z=(((!b[2])?i:(b[2]&1)?j:a[i][j])+c[b[2]]-1)%n+1;
    			Ans[x][y]=z; 
    		}
    		for(int i=1;i<=n;++i){
    			for(int j=1;j<=n;++j)printf("%d ",Ans[i][j]);
    			printf("
    ");
    		}
    		puts("");
    	}
    	return 0;
    } 
    

    D.Flip and Reverse

    给出一个长度为(n)的串(S),一次操作可以选择一个(01)个数相同的区间,将区间取反再反转,求给出字符串经过任意操作后能够得到的字典序最小的串。

    (1 le n le 2cdot10^5)

    solution

    这种题的一般套路是找到某种工具来刻画给出的操作

    记起点为(x=0),从左到右扫描,字符(1)就是(x o x+1), 字符(0)就是(x o x-1) ,那么最终(S)是一个从(0)(t)的一条特定的欧拉路径,其中(t)为最后的(x)

    一次操作其实就是将一个环反向,可以证明允许随意操作就是等价于所有的欧拉路径都是合法的。最小的串就是要求带边权的最小字典序的欧拉路径,直接dfs即可。

    时间复杂度:(O(n))

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1000010;
    int T,n,L[N],R[N],t,tot;
    char s[N],ans[N];
    void dfs(int x){
    	while(L[x+n])L[x+n]--,dfs(x-1),ans[++tot]='0';
    	while(R[x+n])R[x+n]--,dfs(x+1),ans[++tot]='1';
    }
    int main(){
    	//freopen("d.in","r",stdin);
    	//freopen("d.out","w",stdout);
    	scanf("%d",&T);
    	while(T--){
    		scanf("%s",s+1);
    		n=strlen(s+1);t=tot=0;
    		for(int i=-n;i<=n;++i)L[i+n]=R[i+n]=0;
    		for(int i=1;i<=n;++i){
    			if(s[i]=='1')R[t+n]++,t++;
    			else L[t+n]++,t--;
    		}
    		dfs(0);
    		reverse(ans+1,ans+tot+1);
    		for(int i=1;i<=tot;++i)putchar(ans[i]);
    		puts("");
    	}
    	return 0;
    } 
    

    E. Nim Shortcuts

    二维的(nim)游戏,增加了(n)个shortcut点((x_i,y_i)) ,规定这些点是必败的。给出(m)个初始状态((a_i,b_i)) ,询问是否有必胜策略。

    $1 le n,m le 10^5 , 1 le a_i,b_i,x_i,y_i le 10^9 $

    solution

    将状态转化成一个二维平面,显然shortcut点是必败的。而当一个点被确定为必败的,那么它右方和上方的非shortcut点都是必胜的;一个非shortcut是必败的当且仅后继中没有一个点是必胜的。

    我们通过下面的操作来找出所有的非shortcut的必败点,从((0,0))出发,假设当前在((x,y)),依次进行下列判断:

    • 1 ((x,y))是一个shortcut点,移动到((x+1,y+1))
    • 2 ((x,y))的左方有一个shortcut点,移动到((x,y+1))
    • 3 ((x,y))的下方有一个shortcut点,移动到((x+1,y))
    • 4 123均不满足,则((x,y))是一个非shortcut的必败点,移动到((x+1,y+1))

    最后所有的必败点就是shortcut点和非shortcut的必败点,询问直接用map查询,但是坐标的范围很大

    注意到中间的很多点是可以直接跳过的,可以找到shortcut点中当前横坐标(x)的后继(X)和纵坐标(y)的后继(Y),取(Delta =min{X-x,Y-y}) 记录((x,y, Delta))表示((x,y),(x+1,y+1),dots,(x+Delta-1,y+Delta-1))的点都是必败的,然后直接跳到((x+Delta,y+Delta)) 查询同样用map

    时间复杂度:(O((n+m)log n ))

    我太懒了没有写~~~
    

    F. Range Diameter Sum

    给出一颗(n)个点的树和一个关于点的排列,求所有连续区间点集的直径和。

    $1 le n le 10^5 $

    solution

    对于所有边((u,v)),新建一个点(w),将一条边拆成两条边((u,w)(w,v)) ,记原来的点为实点,新加的点为虚点。记树上的一个圆(C(v,r))表示到(v)的树上距离不超过(r)的点的集合。拆边之后,对于一个实点集合(S),一定能够找到唯一的((v,r)),满足(r)最小,$ S subseteq C(v,r)$。(进一步可以证明 (v)(S) 任一直径的中点, (r) 是直径的 (frac{1}{2})

    类似几何中的圆,合并两个集合(C(v_1,r_1))(C(v_2,r_2)) 有下面三种情况:

    • 1 (dis(v_1,v_2)+r_2le r_1),则$C(v_1,r_1)cup C(v_2,r_2) = C(v_1,r_1) $
    • 2 (dis(v_1,v_2)+r_1le r_2),则(C(v_1,r_1)cup C(v_2,r_2) = C(v_2,r_2))
    • 3 不满足1 2 ,如果新的集合为(C(v^{'},r^{'})),那么(r^{'}=frac{1}{2}(dis(v_1,v_2)+r_1+r_2))(v^{'})(v_1 o v_2)路径上到(v_1)距离为(frac{1}{2}(dis(v_1,v_2)-r_1+r_2))的点

    接下来考虑分治解决原问题:

    设当前区间为([l,r])(m=frac{1}{2}(l+r)) ,记(Cl_{i})为覆盖实点集合((i,i+1,dots,m)) 的最小圆,(Cr_{j})为覆盖实点集合((m+1,m+2,cdots,j)) 的最小圆,枚举(i=m o l),考虑如何计算所有的(j=m+1 o r)的答案。

    对于(Cl_i(v_i,r_i))(Cr_j(v_j,r_j))(C^{'}=Cl_{i} cup Cr_{j}) ,只需要计算(r')即可,而由于(j)是不断变大的,所以会存在一个区间([L,R]),区间左边满足情况1,区间后边满足情况2,区间满足情况3。现在就只需要考虑计算3的$ sum_{j=L}^{R} dis(v_i,v_j) $ 的值,因为(i)是不断减小的,以为着(L,R)都是单调增加的,可以用动态点分治维护一个滑动窗口实现。

    ​ 好毒瘤蛙。。。

    退役老年选手是写不出来点分治的。。。
    
  • 相关阅读:
    vue之插槽
    微信公众号-关注和取消关注
    微信公众号-消息响应
    微信公众号-验证接入
    微信公众号-开发工具natapp内网穿透安装和使用
    windows安装PHP5.4+Apache2.4+Mysql5.5
    php各种主流框架的优缺点总结
    php框架的特性总结
    什么是php?php的优缺点有哪些?与其它编程语言的优缺点?
    二进制、八进制、十进制、十六进制之间转换
  • 原文地址:https://www.cnblogs.com/AUSTIN-tkys/p/14195457.html
Copyright © 2020-2023  润新知