【NOIP提高组A】矩阵游戏
题目描述
Input
Output
Sample Input
Sample Input1
3 4 4
R 2 4
S 4 1
R 3 2
R 2 0
Sample Input2
2 4 4
S 2 0
S 2 3
R 1 5
S 1 3
Sample Output
Sample Output1
94
Sample Output2
80
Data Constraint
题解
最朴素的算法
O
(
n
2
)
O(n^{2})
O(n2)是过不了的。
然后发现了这道题操作的顺序是不影响最后的结果的,所以对每行每列的操作可以一起存下来。
既是定义
H
[
i
]
H[i]
H[i]为第
i
i
i行上的数变成了原来的
i
i
i倍,
L
[
i
]
L[i]
L[i]为第
i
i
i列上的数变成了原来的
i
i
i倍
显然
H
[
]
,
L
[
]
H[],L[]
H[],L[]开始全为1,然后随着输入更新就行了。
并且我们可以通过一个数的坐标算出它的值。
n
u
m
[
i
]
[
j
]
=
(
i
−
1
)
∗
m
+
j
num[i][j]=(i-1)*m+j
num[i][j]=(i−1)∗m+j
a
n
s
=
∑
i
=
1
n
∑
j
=
1
m
n
u
m
[
i
]
[
j
]
∗
H
[
i
]
∗
L
[
j
]
ans=\sum_{i=1}^{n}\sum_{j=1}^{m}num[i][j]*H[i]*L[j]
ans=i=1∑nj=1∑mnum[i][j]∗H[i]∗L[j]
a
n
s
=
∑
i
=
1
n
∑
j
=
1
m
(
(
i
−
1
)
∗
m
+
j
)
∗
H
[
i
]
∗
L
[
j
]
ans=\sum_{i=1}^{n}\sum_{j=1}^{m}((i-1)*m+j)*H[i]*L[j]
ans=i=1∑nj=1∑m((i−1)∗m+j)∗H[i]∗L[j]
a
n
s
=
∑
i
=
1
n
∑
j
=
1
m
(
(
i
−
1
)
∗
m
∗
H
[
i
]
∗
L
[
j
]
+
j
∗
H
[
i
]
∗
L
[
j
]
)
ans=\sum_{i=1}^{n}\sum_{j=1}^{m}((i-1)*m*H[i]*L[j]+j*H[i]*L[j])
ans=i=1∑nj=1∑m((i−1)∗m∗H[i]∗L[j]+j∗H[i]∗L[j])
a
n
s
=
∑
j
=
1
m
L
[
j
]
∑
i
=
1
n
(
i
−
1
)
∗
m
∗
H
[
i
]
+
∑
i
=
1
n
H
[
i
]
∑
j
=
1
m
j
∗
L
[
j
]
ans=\sum_{j=1}^{m}L[j]\sum_{i=1}^{n}(i-1)*m*H[i]+\sum_{i=1}^{n}H[i]\sum_{j=1}^{m}j*L[j]
ans=j=1∑mL[j]i=1∑n(i−1)∗m∗H[i]+i=1∑nH[i]j=1∑mj∗L[j]
所以先分别算出 ∑ i = 1 n ( i − 1 ) ∗ m ∗ H [ i ] \sum_{i=1}^{n}(i-1)*m*H[i] ∑i=1n(i−1)∗m∗H[i]和 ∑ j = 1 m j ∗ L [ j ] \sum_{j=1}^{m}j*L[j] ∑j=1mj∗L[j]然后再分别统计答案再求和就行了
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
typedef long long LL;
using namespace std;
const int MAXN=int(1e6+5);
#define mod int(1e9+7)
LL n,m,k;
LL x,y;
LL H[MAXN],L[MAXN];
LL S0,S1;
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
char s[10];
scanf("%lld%lld%lld",&n,&m,&k);
LL x,y;
for(LL i=1;i<=n;i++)
H[i]=1;
for(LL i=1;i<=m;i++)
L[i]=1;
while(k--) {
scanf("%s%lld%lld",s,&x,&y);
if(s[0]=='R') H[x]=H[x]*y%mod;
else L[x]=L[x]*y%mod;
}
for(LL i=1;i<=n;i++)
S0=(S0+(i-1)*m%mod*H[i]%mod)%mod;
for(LL i=1;i<=m;i++)
S1=(S1+i*L[i]%mod)%mod;
LL ans=0;
for(LL i=1;i<=m;i++)
ans=(ans+S0*L[i]%mod)%mod;
for(LL i=1;i<=n;i++)
ans=(ans+S1*H[i]%mod)%mod;
printf("%lld",ans);
}
【NOIP提高组A】跳房子
题目描述
Input
Output
Sample Input
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
Sample Output
4 2
1 3
1 4
Data Constraint
题解
受到中山纪中讲题时常不喜欢给正解的影响(虽然这道题给了),好吧我太菜了,讲一下我考场上的思路。因为
k
<
=
1
0
9
k<=10^{9}
k<=109,所以我们需要找到一个环使
k
k
k变小。再看看找环这个思路虽然容易被卡,但是还算可做,所以就决定直接找环。
具体实现上为了减少在更改后重新找环的过程,我们把一堆
c
h
a
n
g
e
change
change弄成一块,一堆
m
o
v
e
move
move看成一块,然后每次更改后的第一次
m
o
v
e
move
move重新找环就行了,据说是可以过85因为数据太水了没有专门卡这个(其实可以卡成
O
(
n
2
Q
)
O(n^{2}Q)
O(n2Q))
代码
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN=int(2e3+5);
int n,m,Qm;
int a[MAXN][MAXN];
bool vis[MAXN][MAXN];
struct node {
int x,y;
node() {};
node(int X,int Y):x(X),y(Y){}
}t[5];
vector<node>Q;
int hug;
node Up(node u) {return node((u.x+n-2)%n+1,u.y%m+1);}
node Mid(node u) {return node(u.x,u.y%m+1);}
node Down(node u) {return node(u.x%n+1,u.y%m+1);}
bool cmp(node p,node q) {
return a[p.x][p.y]>a[q.x][q.y];
}
void Build(node u) {
t[1]=Up(u),t[2]=Mid(u),t[3]=Down(u);
sort(t+1,t+4,cmp);
node nx=t[1];
if(vis[nx.x][nx.y]) {
for(int i=0;i<Q.size();i++)
if(Q[i].x==nx.x&&Q[i].y==nx.y)
hug=i;
return;
}
vis[nx.x][nx.y]=1;
Q.push_back(nx);
Build(nx);
}
int main()
{
freopen("jump.in","r",stdin);
freopen("jump.out","w",stdout);
node now=node(1,1);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
scanf("%d",&Qm);
char s[10];
int x,y,z,k;
int d=0;
bool f=1;
for(int i=1;i<=Qm;i++) {
scanf("%s",s);
if(s[0]=='c') {
scanf("%d%d%d",&x,&y,&z);
a[x][y]=z;
f=1;
}
else {
scanf("%d",&k);
if(f) {
while(Q.size()!=0) {
node q=Q.back();
Q.pop_back();
vis[q.x][q.y]=0;
}
Q.push_back(now);d=0;
vis[now.x][now.y]=1;
Build(now);
}
int siz=Q.size();
if(d+k<siz) d+=k,k=0;
if(d+k>=hug&&k) k-=hug-d,d=hug;
if(k)
d=hug+(d-hug+1+k)%(siz-hug)-1;
now=Q[d];
printf("%d %d\n",now.x,now.y);
f=0;
}
}
}
【NOIP提高组A】优美序列
题目描述
Input
Output
Sample Input
Sample Input1
7
3 1 7 5 6 4 2
3
3 6
7 7
1 3
Sample Input2
10
2 1 4 3 5 6 7 10 8 9
5
2 3
3 7
4 7
4 8
7 8
Sample Output
Sample Output1
3 6
7 7
1 7
Sample Output2
1 4
3 7
3 7
3 10
7 10
Data Constraint
题解
大概来说,据说,可能可以这样说,这个方法可有卡过(有人写出了985ms)。
就是对原数列我们维护一个它的映射,既对
a
[
i
]
=
j
a[i]=j
a[i]=j存
i
d
[
j
]
=
i
id[j]=i
id[j]=i
所以对
a
a
a中一段区间
[
L
,
R
]
[L,R]
[L,R]来说,找到他的
M
a
x
,
M
i
n
Max,Min
Max,Min,再找到它在
i
d
id
id上的值,假如对应了
L
′
,
R
′
L',R'
L′,R′
所以
i
d
id
id中
[
L
′
,
R
′
]
[L',R']
[L′,R′]中的都要取,我们找到其中位置最大的和最小的,就是重新把值对应回位置,这样就得到了一个新的
a
a
a中的区间
[
L
,
R
]
′
[L,R]'
[L,R]′,不断重复这个操作直到找到一个合法区间就行了。
简单说一下,合法区间就是值对应位置和位置都是值都是匹配的。
最大值最小值搞一个
S
T
ST
ST表就行了。
代码
#pragma GCC optimize(2)
#include<cmath>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
int read(){
int x=0;bool f=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=x*10+(c^48),c=getchar();
return f?-x:x;
}
void print(int x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
#define MAXN 100000
int N,M;
int A[MAXN+5],pos[MAXN+5];
int Log2[MAXN+5],Pow2[MAXN+5];
int Ansl,Ansr;
#define LOG 17
int STMax0[MAXN+5][LOG+5],STMax1[MAXN+5][LOG+5];
int STMin0[MAXN+5][LOG+5],STMin1[MAXN+5][LOG+5];
void Init(){
for(int i=1;i<=N;i++)
Log2[i]=log2(i);
for(int i=0;i<=LOG;i++)
Pow2[i]=1<<i;
for(int i=1;i<=N;i++)
STMax0[i][0]=STMin0[i][0]=A[i],
STMax1[i][0]=STMin1[i][0]=pos[i];
for(int j=1;j<=LOG;j++)
for(int i=1;i+Pow2[j-1]<=N;i++)
STMax0[i][j]=max(STMax0[i][j-1],STMax0[i+Pow2[j-1]][j-1]),
STMin0[i][j]=min(STMin0[i][j-1],STMin0[i+Pow2[j-1]][j-1]),
STMax1[i][j]=max(STMax1[i][j-1],STMax1[i+Pow2[j-1]][j-1]),
STMin1[i][j]=min(STMin1[i][j-1],STMin1[i+Pow2[j-1]][j-1]);
}
int QueryMax0(int l,int r){
int k=Log2[r-l+1];
return max(STMax0[l][k],STMax0[r-Pow2[k]+1][k]);
}
int QueryMax1(int l,int r){
int k=Log2[r-l+1];
return max(STMax1[l][k],STMax1[r-Pow2[k]+1][k]);
}
int QueryMin0(int l,int r){
int k=Log2[r-l+1];
return min(STMin0[l][k],STMin0[r-Pow2[k]+1][k]);
}
int QueryMin1(int l,int r){
int k=Log2[r-l+1];
return min(STMin1[l][k],STMin1[r-Pow2[k]+1][k]);
}
void Solve(int l,int r){
int Max=QueryMax0(l,r),Min=QueryMin0(l,r);
int L=QueryMin1(Min,Max),R=QueryMax1(Min,Max);
Max=QueryMax0(L,R),Min=QueryMin0(L,R);
if(Max-Min==R-L){
Ansl=L,Ansr=R;
return;
}
Solve(L,R);
}
int main(){
freopen("sequence.in" ,"r", stdin);
freopen("sequence.out","w",stdout);
N=read();
for(int i=1;i<=N;i++)
pos[A[i]=read()]=i;
Init();
M=read();
while(M--){
int L=read(),R=read();
Solve(L,R);
print(Ansl),putchar(' '),print(Ansr),putchar('\n');
}
}