20220929

20220928

传送门

t1 牛牛的方程式

思路

​ 由裴蜀定理:若\(a,b\)为整数,且\(gcd(a,b)=d\),那么一定存在整数\(x,y\)使得\(ax+by=d\)成立。这样,这道题就迎刃而解了。代码也非常简单,就不放上来了。唯一需要注意的就是运算过程中0可能会作为除数。

t2 牛牛的猜球游戏

思路1 线段树

​ 可以发现对于一个操作区间\([l,r]\),依次进行单个操作与先进行区间\([l,k]\),再进行区间\([k,r]\)的操作是一样的。所以我们可以考虑用线段树维护这个操作,在进行区间合并时,我们只需将左区间\([l,k]\)的合并结果按照右区间的合并结果进行交换即可。为什么这样是正确的呢?其实也很简单,因为一个区间\([l,r]\)的合并结果与将该区间的操作顺次进行是等效的,所以区间\([l,k],[k,r]\)都与其所含操作是等效的,将区间\([l,k]\)按区间\([k,r]\)交换与按区间\([k,r]\)包含的操作逐次进行也是等效的。

代码如下=) 可能代码有亿点丑陋:(

点击查看代码
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
template<typename T>inline void in(T &x){
    x=0;int f=0;char c=getchar();
    for(;!isdigit(c);c=getchar())f|=(c=='-');
    for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
    x=f?-x:x;
}
template<typename T>inline void out(T x){
    if(x<0)x=~x+1,putchar('-');
    if(x>9)out(x/10);
    putchar(x%10^48);
}
const int N=100005;
int n,m;
struct move{
    int a,b;
}act[N];
struct po{
    int pos[10];
    void init(){
	fo(i,0,9)pos[i]=i;
    }
    bool check(){
	fo(i,0,9)if(pos[i]!=i)return 0;
	return 1;
    }
    void outt(){
	fo(i,0,9){
	    if(i==9)out(pos[i]),putchar('\n');
            else out(pos[i]),putchar(' ');
	}
    }
};
struct node{
    int l,r,lch,rch;
    po p;
}rt[N<<1];
int tot;
inline void merge(int x,int l,int r){
    fo(i,0,9)
	rt[x].p.pos[i]=rt[l].p.pos[rt[r].p.pos[i]];
}
inline po amerge(po a,po b){
    po ans;
    fo(i,0,9)
	ans.pos[i]=a.pos[b.pos[i]];
    return ans;
}
inline int build(int l,int r){
    ++tot;
    int x=tot;
    rt[x].l=l;
    rt[x].r=r;
    if(l==r){
	rt[x].p.init();
	rt[x].p.pos[act[l].a]=act[l].b;
	rt[x].p.pos[act[l].b]=act[l].a;
	return x;
    }
    int mid=(l+r)>>1;
    rt[x].lch=build(l,mid);
    rt[x].rch=build(mid+1,r);
    merge(x,rt[x].lch,rt[x].rch);
    return x;
}
inline po query(int x,int l,int r){
    if(l<=rt[x].l&&rt[x].r<=r)return rt[x].p;
    int mid=(rt[x].l+rt[x].r)>>1;
    po tmp;
    tmp.init();
    if(l<=mid)tmp=query(rt[x].lch,l,r);
    if(r>mid)tmp=amerge(tmp,query(rt[x].rch,l,r));
    return tmp;
}
po ans;
int l,r;
int main(){
    in(n),in(m);
    fo(i,1,n)in(act[i].a),in(act[i].b);
    build(1,n);
    fo(i,1,m){
	in(l),in(r);
	ans=query(1,l,r);
	ans.outt();
    }
    return 0;
}

思路2 广义前缀和

​ 我们考虑如何用类似于前缀和的形式\(sum[r]-sum[l-1]\)求出操作区间\([l,r]\)的答案。我们可以用类似于前缀和的形式一个数组(结构体,或vector)存下每次操作后的状态。然后再考虑如何在相减时抵消之前操作的影响。由于每次游戏开始时序号排列一定是\(1—n\),所以可将进行了\(l-1\)次操作后的对应xulie进行标号,相当于一步完成从\(1\)\(l-1\)的所有操作。然后再通过类似的操作,用\(r\)的状态还原出答案。

点击查看代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=200005;
int n,m;
int sum[N][10];
int ans[10];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<=9;++i)sum[0][i]=i;
    for(int i=1;i<=n;++i){
        memcpy(sum[i],sum[i-1],sizeof(sum[i-1]));
        int x,y;
        scanf("%d%d",&x,&y);
        swap(sum[i][x],sum[i][y]);
    }
    while(m--){
        int l,r;
        scanf("%d%d",&l,&r);
        int tmp[20];
        for(int i=0;i<=9;++i)tmp[sum[l-1][i]]=i;
        for(int i=0;i<=9;++i)ans[i]=tmp[sum[r][i]];
        for(int i=0;i<=9;++i)printf("%d%c",ans[i],i==9?' ':'\n');
    }
    return 0;
}

t3 牛牛的凑数游戏

t4 牛牛的RPG游戏

posted @ 2022-09-29 07:33  リン・グァン  阅读(11)  评论(0编辑  收藏  举报