中山纪中集训Day5叒是测试

A组T1 矩阵游戏(game) 九校联考24OI__D1T1
问题描述
LZK发明一个矩阵游戏,大家一起来玩玩吧,有一个N行M列的矩阵。第一行的数字是1,2,…M,第二行的数字是M+1,M+2…2*M,以此类推,第N行的数字是(N-1)*M+1,(N-1)M+2…NM。
例如,N=3,M=4的矩阵是这样的:
1 2 3 4
5 6 7 8
9 10 11 12
对于身为智慧之神的LZK来说,这个矩阵过于无趣.于是他决定改造这个矩阵,改造会进行K次,每次改造会将矩阵的某一行或某一列乘上一个数字,你的任务是计算最终这个矩阵内所有数字的和,输出答案对109+7取模。
输入
第一行包含三个正整数N、M、K,表示矩阵的大小与改造次数。接下来的行,每行会是如下两种形式之一:
R X Y,表示将矩阵的第X(1 ≤ X ≤ N)行变为原来的Y(0 ≤ Y ≤10^9)倍.
S X Y,表示将矩阵的第X(1 ≤ X ≤ M)列变为原来的Y(00≤ Y ≤10^9)倍.
输出
输出一行一个整数,表示最终矩阵内所有元素的和对109+7109+7取模的结果。
输入输出样例
样例1
input
3 4 4
R 2 4
S 4 1
R 3 2
R 2 0
output
94
样例2
input
2 4 4
S 2 0
S 2 3
R 1 5
S 1 3
output
80
样例一的解释:操作结束之后矩阵会变成这样:
1 2 3 4
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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值