2019.8.5中山纪中提高A组题解

【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]=(i1)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=1nj=1mnum[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=1nj=1m((i1)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=1nj=1m((i1)mH[i]L[j]+jH[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=1mL[j]i=1n(i1)mH[i]+i=1nH[i]j=1mjL[j]

所以先分别算出 ∑ i = 1 n ( i − 1 ) ∗ m ∗ H [ i ] \sum_{i=1}^{n}(i-1)*m*H[i] i=1n(i1)mH[i] ∑ j = 1 m j ∗ L [ j ] \sum_{j=1}^{m}j*L[j] j=1mjL[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 &lt; = 1 0 9 k&lt;=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&#x27;,R&#x27; L,R
所以 i d id id [ L ′ , R ′ ] [L&#x27;,R&#x27;] [L,R]中的都要取,我们找到其中位置最大的和最小的,就是重新把值对应回位置,这样就得到了一个新的 a a a中的区间 [ L , R ] ′ [L,R]&#x27; [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');
    }
}
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值