zoukankan      html  css  js  c++  java
  • 米缸

    ### Description

      数据范围:(r,c<=2000,m<=5000,1<=M_{ij},c)(操作中的)(,k<=10^9)

      

    Solution

      形式化一下这个移动的过程,考虑如果没有修改,那么对于第(x)列的第(i)行,我们可以预处理出它(move)一步之后会在第(nxt(x))列哪一行,具体一点就是我们可以对于每一列预处理出一个长度为(r)的数组,记为(change[x][i]),有了这个数组之后,我们从(change[1])合并到(change[c])就可以得到一个新的数组(change1),其中(change1[i])表示从第(1)列的第(i)(move)到第(c)列之后在哪一行

    ​  记当前所在位置为((nowx,nowy)),考虑询问,因为列数(c)比较小,所以对于一次(move k)操作,我们可以先暴力将当前的列跳到第(1)列(如果有那么多次的话),这需要(c-nowy+1)步,然后剩下(k-(c-nowy+1))步我们可以先利用(change1)数组一圈一圈地跳(也就是(c)(c)步跳),具体的话就是可以实现一个类似快速幂一样的东西,计算数组({1,2,3,4,...,r})在连续被(change1)作用了(x)次之后会变成什么(记为。。(change1^{x})好了),然后跳完那么多圈之后还是在第(1)列,所以这个时候的位置是((change1^x[nowx'],nowy)),这里的(nowx')是暴力跳到第(1)列之后的所在行数,然后剩下((k-(c-nowy+1))\%c)步也是直接暴力跳就好了

      现在考虑有修改,瓶颈在于要重新计算(change1),但是考虑一次修改对于(change)数组的影响,如果说修改的地方是((x,y)),那么只会影响到(change[nxt(x)])这一个数组的值

      所以我们可以考虑将所有的(change[i])丢进线段树作为线段树的底层节点维护的信息,这样(change1)就是线段树根节点处的值了,对于一次修改直接重新算一遍(change[nxt(x)]),然后将线段树中(nxt(x))这个位置的值更新一下即可

      

      mark:想清楚一次修改的影响

      

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=2010;
    int a[N][N];
    int n,m,q,nowx,nowy;
    struct Data{
    	int p[N];
    	void init(){for (int i=1;i<=n;++i) p[i]=i;}
    	friend Data operator + (Data x,Data y){
    		for (int i=1;i<=n;++i) x.p[i]=y.p[x.p[i]];
    		return x;
    	}
    }change[N];
    namespace Seg{/*{{{*/
    	const int N=::N*4;
    	int ch[N][2];
    	Data info[N];
    	int n,tot;
    	void pushup(int x){info[x]=info[ch[x][0]]+info[ch[x][1]];}
    	void _build(int x,int l,int r){
    		if (l==r){info[x]=change[l];return;}
    		int mid=l+r>>1;
    		ch[x][0]=++tot; _build(ch[x][0],l,mid);
    		ch[x][1]=++tot; _build(ch[x][1],mid+1,r);
    		pushup(x);
    	}
    	void build(int _n){n=_n; tot=1;_build(1,1,n);}
    	void _update(int x,int d,int lx,int rx){
    		if (lx==rx){
    			info[x]=change[lx]; return;
    		}
    		int mid=lx+rx>>1;
    		if (d<=mid) _update(ch[x][0],d,lx,mid);
    		else _update(ch[x][1],d,mid+1,rx);
    		pushup(x);
    	}
    	void update(int d){_update(1,d,1,n);}
    }/*}}}*/
    int X(int x){x=(x+n)%n;return x==0?n:x;}
    int Y(int y){y=(y+m)%m;return y==0?m:y;}
    int work(int x,int y){
    	int nwy=Y(y+1),pre=X(x-1),nxt=X(x+1);
    	int ret=x;
    	if (a[pre][nwy]>a[ret][nwy]) ret=pre;
    	if (a[nxt][nwy]>a[ret][nwy]) ret=nxt;
    	return ret;
    }
    void prework(){
    	int pre,nxt,tmp,nwy;
    	for (int x=1;x<=n;++x)
    		for (int y=1;y<=m;++y)
    			change[y].p[x]=work(x,y);
    	Seg::build(m);
    }
    void modify(int y){
    	for (int i=1;i<=n;++i)
    		change[y].p[i]=work(i,y);
    	Seg::update(y);
    }
    void force_move(int &x,int &y,int tm){
    	for (int i=1;i<=tm;++i)
    		x=change[y].p[x],y=Y(y+1);
    }
    Data ksm(Data &x,int y){
    	Data ret,base=x;
    	ret.init();
    	for (;y;y>>=1,base=base+base)
    		if (y&1) ret=ret+base;
    	return ret;
    }
    void solve(int tm){
    	int tm1=min(tm,m-nowy+1);
    	Data tmp;
    	force_move(nowx,nowy,tm1);
    	tmp=ksm(Seg::info[1],(tm-tm1)/m);
    	nowx=tmp.p[nowx];
    	force_move(nowx,nowy,(tm-tm1)%m);
    	printf("%d %d
    ",nowx,nowy);
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("a.in","r",stdin);
    #endif
    	char op[10];
    	int x,y,z;
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;++i)
    		for (int j=1;j<=m;++j)
    			scanf("%d",&a[i][j]);
    	prework();
    	nowx=1; nowy=1;
    	scanf("%d",&q);
    	for (int i=1;i<=q;++i){
    		scanf("%s",op);
    		if (op[0]=='m'){
    			scanf("%d",&x);
    			solve(x);
    		}
    		else{
    			scanf("%d%d%d",&x,&y,&z);
    			a[x][y]=z;
    			modify(Y(y-1));
    		}
    	}
    }
    
  • 相关阅读:
    P1642 规划 [01分数规划]
    01分数规划学习笔记
    P1527 [国家集训队]矩阵乘法 [整体二分]
    P3292 [SCOI2016]幸运数字 [线性基+倍增]
    java中遍历集合的三种方式
    20190706中兴提前批专业面面经
    《java入门如此简单》——语句,函数和数组
    java中数组常见的操作
    2019 波克城市ava面试笔试题 (含面试题解析)
    2019 华云数据java面试笔试题 (含面试题解析)
  • 原文地址:https://www.cnblogs.com/yoyoball/p/10122493.html
Copyright © 2011-2022 走看看