中山JizhongトレーニングDay5チーズはテストです(ミャオ族を指定)

グループT1行列ゲーム(ゲーム)9つの学校の入学試験24OI__D1T1

問題の説明

LZKの発明は、マトリックスのゲームで、一緒にそれをすべて再生し、N行M列の行列があります。最初の行は数1,2、...とMは、第2の行M + 1、M + 2の数...である 2 * M、 というように、N行(N-1)の数* M + 1 、(N-1)* M + 2 ... N * M.
例えば、N = 3、M = 4は、マトリックスは、このようなことである:
1. 3 2 4
5 6 7 8
9 11 10 12であります

神LZKの知恵として、この行列はあまりにも退屈なので、彼はこの変換行列をすることを決定し、変換がK倍になり、ラインが行列または数を乗じ列を変換しますたびに、あなたのタスクがにあります最終的な数とこのすべて、マトリックス109件の答え+ 7 MODの出力を算出します。

エントリー

最初の行は、3つの正の整数N、M、Kが含まれている回数変換行列のサイズを表します。各ラインは、2つの形式のいずれかであろう次のライン、:
RXY、それはYの元の行に第1のマトリックスX(1≤X≤N)を示している(0  Y≤≤ 10 ^を9回)。
SXY、これは、元の行列(Y列に第一X(1≤X≤M)を表す0 Y≤≦0を10 ^ 9回)。

輸出

最終的な行列は要素のすべてを表し、ライン出力、整数、10 。9 + 7 + 7 109モジュロ結果。

サンプル入力と出力

サンプル1

入力
3 4 4
R 2 4
S 4 1
R 3 2
R 2 0
出力
94

サンプル2

入力
2 4 4
S 2 0
S 2 3
R 1つの5
S 1つの3
出力
80

サンプル説明:この行列を終えた後は次のようになる
。1. 4 3 2
0 0 0 0
18 20 22 24であります

データ範囲

データ満足の40%:1≦N、M≤1000;
データ満足の80%:1≤N、M≤1000000,1≤K≤1000;
100%のデータを満たす:1≤N、M≤1000000,1≤ K≤100000。

クールを装って分析

  ダーク観測された番組

   1.操作は、最終結果に影響を及ぼさない、任意の順序で実行されてもよいです。

   2.各行は等差数列であり、寛容の開始は1です。

   3.各列は、演算シーケンスの全ての公差の許容範囲と数の演算シーケンスの数を加算したカラムです。

   私たちは列を進める前に、先進的なトレッキングの操作を考慮することができます。後数を乗じた各ライン番号は依然として倍数で等差数列、公差対応する増加です。このように、各ラインのために、我々は最初の項のみを使用し、十分にその寛容性を維持します。ラインの動作は、それが実行されたすべての数値の算術和の最初の列を取得し、最初の列を与えるようにし、各列を与えるために公差、及び公差を追加する場合、各列が決定され、残りすることができますこれは、列に直接であると十分に掛けます。最後に質問の気前興奮をカットオフ

 

#include<cstdio>
const int mod=1000000007;
int n,m,k,cnt;char ch[5];
int a[1000005],b[1000005],sum[1000005],d,ans;
struct node{int x,y;}q[100005];
int main()
{
    //freopen("game.in","r",stdin);freopen("game.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);for(int i=1;i<=n;i++)a[i]=(1ll*(i-1)*m+1)%mod,b[i]=1;
    for(int i=1,x,y;i<=k;i++)
    {
        scanf("%s%d%d",ch+1,&x,&y);
        if(ch[1]=='S'){q[++cnt].x=x,q[cnt].y=y;continue;}
        a[x]=(1ll*a[x]*y)%mod;b[x]=(1ll*b[x]*y)%mod;
    }
    for(int i=1;i<=n;i++)sum[1]=(sum[1]+a[i])%mod,d=(d+b[i])%mod;
    for(int i=2;i<=m;i++)sum[i]=(sum[i-1]+d)%mod;
    for(int i=1;i<=cnt;i++)sum[q[i].x]=(1ll*sum[q[i].x]*q[i].y)%mod;
    for(int i=1;i<=m;i++)ans=(ans+sum[i])%mod;
    printf("%d\n",ans);
}

 

 

 

A组T2 跳房子(jump)跳楼  

 ↑

导致我几天更不了博客的万恶之源

题目描述

跳房子,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。
跳房子是在N个格子上进行的,CYJ对游戏进行了改进,该成了跳棋盘,改进后的游戏是在一个N行M列的棋盘上进行,并规定从第一行往上可以走到最后一行,第一列往左可以走到最后一列,反之亦然。每个格子上有一个数字。
在这个棋盘左上角(1,1)放置着一枚棋子。每次棋子会走到右、右上和右下三个方向格子中对应上数字最大一个。即任意时刻棋子都只有一种走法,不存在多个格子同时满足条件。
现在有两种操作:
move k将棋子前进k步。
change a b e将第a行第b列格子上的数字修改为e。
请对于每一个move操作输出棋子移动完毕后所处的位置。

输入格式

第一行包含两个正整数N,M,表示棋盘的大小。
接下来N行,每行M个整数,依次表示每个格子中的数字a[i,j]。
接下来一行包含一个正整数Q,表示操作次数。
接下来m行,每行一个操作。

输出格式

对于每个move操作,输出一行两个正整数x,y,即棋子所处的行号和列号。

样例

样例输入

4 4
1 2 9 3
3 5 4 8
4 3 2 7
5 8 1 6
4
move 1
move 1
change 1 4 100
move 1

样例输出

4 2
1 3
1 4

数据范围与提示

10的数据满足:3⩽N,M⩽50,Q⩽5,000,k⩽50;
20的数据满足:3⩽N,M⩽200,Q⩽5,000,k⩽5,000;
另有20的数据满足:3⩽N,M⩽200,Q⩽5,000,k⩽10^9;
100的数据满足:3⩽N,M⩽2,000,Q⩽5,000,e,k⩽10^9;

看完题解才知道如何分析

  首先,图中肯定有环。但如果选择维护所有点是否在环上,一次更改操作复杂度就爆炸了。(于是考场上我选择一步一步走)其实只需要选一个折中的办法,维护第一列的每个点走n步回到第一列会到达哪个位置,以及第一列每个点是否在环上和环的长度即可(这是个基环树,只会有一个环)。

   对于每次移动操作,先暴力移动到第一列,再一行一行的跳,若跳到环则直接让 步数 模 环的长度,最后仍然一次跳一行,步数不足一行就暴力跳。

   对于每次修改操作,先判断修改的点的前面三个点中,哪些是会因为它的改变而改变路径,在倒退看第一列哪些点会经过这三个点,修改要维护的信息。

   修改时每个点都倒退回去显然是会TLE的,但我们发现两个相邻的点路径无法交叉,就像这样:

 

       显然这样的情况是不合法的。既然路径无法交叉,我们就只用维护受影响的点中两边上的几个点,就可以得到受影响的点的左右边界。(因为移动可能会越过边界,所以左边界可能比右边界还要大,这时维护矩阵两侧的点就好了)。讲得比我详细不知多少倍的原博客:https://www.cnblogs.com/wzc521/p/11310817.html

    代码(这道题写的我想去跳楼了,所以不要在意那个O2)

#pragma GCC optimize(2)
#include<cstdio>
int n,m,q,len,dx[]={-1,0,1};char ch[10];
int cir[4050],vis[4050],sta[4050],inst[4050],jump[4050],mart[4050][4050];
struct node{int x,y;}ans,p[50];
inline node nex(node o)
{
    int maxx=0,nex,y=o.y%m+1;
    for(int k=0,x;k<3;k++)if(mart[x=(o.x+dx[k]-1+n)%n+1][y]>maxx)maxx=mart[nex=x][y];
    return (node){nex,y};
}
inline node walk(node o,int k){if(!k)return o;return walk(nex(o),k-1);}
inline void judge(int x)
{
    vis[x]=inst[x]=1;sta[++sta[0]]=x;
    if(inst[jump[x]])
    {
        int y;len=0;
        do{inst[y=sta[sta[0]--]]=0;cir[y]=1;len+=m;}while(y!=jump[x]);
    }
    else if(!vis[jump[x]])judge(jump[x]);
    inst[x]=0;
}
inline void move(int k)
{
    while(k&&ans.y!=1)k--,ans=nex(ans);
    while(k>=m&&!cir[ans.x])k-=m,ans.x=jump[ans.x];
    k%=len;while(k>=m)k-=m,ans.x=jump[ans.x];
    while(k--)ans=nex(ans);
}
inline int fix(int x,int y,int k1)
{
    if(y==1)return x;int befx=-1;
    node bef;bef.y=(y-1-1+m)%m+1;
    for(int k=0;k<=2&&befx==-1;k++)
    bef.x=(x+k1*dx[k]-1+n)%n+1,befx=nex(bef).x==x&&nex(bef).y==y?fix(bef.x,bef.y,k1):-1;
    return befx;
}
inline void change(int a,int b,int e)
{
    int v[5][5]={0},y=(b-1-1+m)%m+1;
    for(int k=0;k<3;k++)p[k].x=(a-dx[k]-1+n)%n+1,p[k].y=y,v[k][0]=(nex(p[k]).x==a&&nex(p[k]).y==b);
    mart[a][b]=e;for(int k=0;k<3;k++)v[k][1]=(nex(p[k]).x==a&&nex(p[k]).y==b);
    for(int k=0,tot;k<3;k++)if(v[k][0]!=v[k][1])
    {
        tot=walk(p[k],m-p[k].y+1).x;
        int l=fix(p[k].x,p[k].y,1),r=fix(p[k].x,p[k].y,-1);
        if(l==-1||r==-1)continue;
        for(int i=l;i!=r;i=i%n+1)jump[i]=tot;jump[r]=tot;
    }
}
int main()
{
    //freopen("jump.in","r",stdin);freopen("jump.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&mart[i][j]);
    for(int i=1;i<=n;i++)jump[i]=walk((node){i,1},m).x;
    for(int i=1;i<=n;i++)if(!vis[i])judge(i);
    scanf("%d",&q);ans.x=ans.y=1;
    while(q--)
    {
        scanf("%s",ch);
        if(ch[0]=='m')
        {
            int k;scanf("%d",&k);
            move(k);
            printf("%d %d\n",ans.x,ans.y);
        }
        else
        {
            int a,b,e;scanf("%d%d%d",&a,&b,&e);
            change(a,b,e);
            for(int i=1;i<=n;i++)vis[i]=cir[i]=0;
            sta[0]=0;
            for(int i=1;i<=n;i++)if(!vis[i])judge(i);
        }
    }
}

 A组T3 优美序列(sequence)

问题描述

Lxy养了N头奶牛,他把N头奶牛用1..N编号,第i头奶牛编号为i。为了让奶牛多产奶,每天早上他都会让奶牛们排成一排做早操。奶牛们是随机排列的。在奶牛排列中,如果一段区间[L,R]中的数从小到大排列后是连续的,他认为这段区间是优美的。比如奶牛排列为:(3, 1, 7, 5, 6, 4, 2),区间[3,6]是优美的,它包含4,5,6,7连续的四个数,而区间[1,3] 是不优美的。Lxy的问题是:对于给定的一个区间[L,R](1<=L<=R<=N), 他想知道,包含区间[L,R]的最短优美区间,比如区间[1,3]的最短优美区间是[1,7]。

输入

第一行为一个整数N,表示奶牛的个数。
第二行为1到N的一个排列,表示奶牛的队伍。
第三行为一个整数M,表示有M个询问。
后面有M行,每行有两个整数L,R表示询问区间。

输出

输出为M行,每行两个整数,表示包含询问区间的最短优美区间。

输入输出样例

样例1

input
7
3 1 7 5 6 4 2
3
3 6
7 7
1 3
output
3 6
7 7
1 7

样例2

input
10
2 1 4 3 5 6 7 10 8 9
5
2 3
3 7
4 7
4 8
7 8
output
1 4
3 7
3 7
3 10
7 10

数据范围

15%的数据满足: 1 <=N,M <= 15;
50%的数据满足:1 <=N,M <= 1000。
100%的数据满足:1 <=N,M <= 100000。

根本不是正解的分析

  在一个大佬的博客看到了80分的算法:(原博客:https://www.cnblogs.com/Ch-someone/p/9664616.html

  “ 

     解析

 

       为了给出题人留面子,不让他被骂出个题只有一种解法而且只有满分解法,我先给个80分做法。
      预处理一个数组pos[i],表示数i在原数组的位置(第几个数)。
      在询问区间内找一个最小值和最大值,作为新的l和r,新的l和r作为一个询问区间,在pos数组里找一个最小值和最大值,他们再作为新的l和r,形成询问区间,在原数组寻找最小值和最大          值,并再次重复刚才的操作……
      上述过程可能很无脑很中二,然而这就是模拟题意找出答案的过程。
      最大值最小值用st表预处理,一共四个st表,比线段树好写……
      那么…… 
      正解是分治,像归并排序那样。
      不断求[l,mid] [mid+1,r]的优美序列,直到分成[mid,mid+1]
      可以证明是正确的。
      事实上分治做的熟练的人可能一眼秒这道题。

   ”

  于是,某个同学把这个80分解法拿过来加了个优化,然后就过了。。。。。

  优化是这样的:对于任意一个区间(l,r),它的最短优美区间一定包含它子区间的最短优美区间,所以用一个st表预处理以i为左端点,长度为2^j的区间的最短优美区间即可。

  代码:

#include<cstdio>
#include<algorithm>
const int maxn=2e5+5;
using namespace std;
struct node{int x,y;node(int x=131072,int y=0):x(x),y(y){}}ans,pre[maxn][18];
int n,m,a[maxn],log[maxn],pos[maxn];
int mx[maxn][18],mn[maxn][18],mxp[maxn][18],mnp[maxn][18];
inline void print(int x){if(x>9)print(x/10);putchar(x%10+48);}
inline int read(){register int x=0;char ch;while(ch<'0'||ch>'9')ch=getchar();while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x;}
inline node solve(int l,int r)
{
    int minn,maxx,k,k1;k=log[r-l+1];
    while((maxx=max(mx[l][k],mx[r-(1<<k)+1][k]))-(minn=min(mn[l][k],mn[r-(1<<k)+1][k]))!=r-l)
    k1=log[maxx-minn+1],l=min(mnp[minn][k1],mnp[maxx-(1<<k1)+1][k1]),
    r=max(mxp[minn][k1],mxp[maxx-(1<<k1)+1][k1]),k=log[r-l+1];
    return node(l,r);
}
void prework()
{
    for(int i=1;i<=n;i++)pre[i][0]=node(i,i);
    for(register int i=1;i<=log[n];i++)for(register int j=1;j<=n&&j+(1<<(i-1))<=n;j++)
    pre[j][i]=solve(min(pre[j][i-1].x,pre[j+(1<<(i-1))][i-1].x),max(pre[j][i-1].y,pre[j+(1<<(i-1))][i-1].y));
}
int main()
{
    //freopen("sequence.in","r",stdin);freopen("sequence.out","w",stdout);
    scanf("%d",&n);
    for(register int i=1;i<=n;i++)a[i]=read(),mxp[a[i]][0]=mnp[a[i]][0]=pos[mn[i][0]=mx[i][0]=a[i]]=i,log[i+1]=log[(i+1)/2]+1;;
    for(register int i=1;i<=log[n];i++)for(register int j=1;j<=n&&j+(1<<(i-1))<=n;j++)
    mn[j][i]=min(mn[j][i-1],mn[j+(1<<(i-1))][i-1]),mx[j][i]=max(mx[j][i-1],mx[j+(1<<(i-1))][i-1]),
    mnp[j][i]=min(mnp[j][i-1],mnp[j+(1<<(i-1))][i-1]),mxp[j][i]=max(mxp[j][i-1],mxp[j+(1<<(i-1))][i-1]);
    prework();
    scanf("%d",&m);
    for(register int i=1,l,r,k;i<=m;i++)
    {
        l=read(),r=read(),k=log[r-l+1];
        ans=solve(min(pre[l][k].x,pre[r-(1<<k)+1][k].x),max(pre[r-(1<<k)+1][k].y,pre[l][k].y));
        print(ans.x);putchar(' ');print(ans.y);puts("");
    }
}

 

おすすめ

転載: www.cnblogs.com/firecrazy/p/11332810.html