Logo S08577 的博客

博客

标签
暂无

8.6 mx 训练赛记录

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-08-06 21:20:54

A

题意

P1516

思路

设青蛙出发点为 $m1$ 和 $n1$ ,分别能跳 $m$ 米和 $n$ 米,周长为 $l$,不难发现:

$$x(m-n)\equiv m1-n1 \ ( \bmod \ l )$$

移项得:

$$x(m-n)+ly= m1-n1$$

不妨设 $a=m-n$,$b=l$,$c=m1-n1$

得:

$$ax+by=c$$

若该方程有解,则 $\gcd(a,b) \mid c$ 是必要条件。

可以用exgcd求解。(注意要处理负数的情况)

代码

int exgcd(int a,int b,int &x,int &y){
    if(b==0){
        x=1,y=0;
        return a;
    }
    d=exgcd(b,a%b,x,y);
    
    int xx=x;
    x=y;
    y=xx-(a\/b)*y;
    return d;
}

signed main(){
    
\/\/    freopen("road.in","r",stdin);
\/\/    freopen("road.out","w",stdout);

    int x,y,m,n,l;
    cin>>x>>y>>m>>n>>l;
    if(n<m){
        swap(n,m);
        swap(x,y);
    }
    int a=x-y;
    int b=n-m;
    int t=exgcd(b,l,x,y);
   \/\/ cout<<a<<' '<<b<<' '<<c<<' '<<t<<endl;
    if(a%t){
        cout<<"Impossible";
        return 0;
    }

    int md=l\/t;
    x=(x*(a\/d)%md+md)%md;
    cout<<x;
    return 0;
}

B

题意

P3868

思路

比较板子。

由题意得:

$$\left\{\begin{matrix} & n-a_1 \equiv 0 \ \pmod{b_1} \ & n-a_2 \equiv 0 \ \pmod{b_2} \ & \dotsb \ & n-a_k \equiv 0 \ \pmod{b_k} \end{matrix}\right.$$

移项得:

$$\left\{\begin{matrix} & n \equiv a_1 \ \pmod{b_1} \ & n \equiv a_2 \ \pmod{b_2} \ & \dotsb \ & n \equiv a_k\ \pmod{b_k} \end{matrix}\right.$$

中国剩余定理板子。

注意这道题要预处理负数的情况并且使用快速乘。

代码

int exgcd(int a,int b,int &x,int &y){
    if(b==0){
        x=1,y=0;
        return a;
    }
    d=exgcd(b,a%b,x,y);
    
    int xx=x;
    x=y;
    y=xx-(a\/b)*y;
    return d;
}

long long ksm(long long a,long long b){
    long long res=0;
    while(b>0){
        if(b&1)
            res=(res+a+lcm)%lcm;
        a=(a+a)%lcm;
        b>>=1;
    }
    return res%lcm;
}

int crt(){
    int ans=0,mi,x,y,d;
    for(int i=1;i<=n;i++){
        mi=lcm\/b[i];
        exgcd(mi,b[i],x,y);
        x=(x%b[i]+b[i])%b[i];
        ans=((ans+ksm(mi,ksm(x,a[i])))%lcm+lcm)%lcm;
       \/\/ ans=(ans+(mi*x*b[i])%lcm+lcm%lcm)%lcm;
    }
    return (ans+lcm)%lcm;
}


signed main(){
    
\/\/    freopen("road.in","r",stdin);
\/\/    freopen("road.out","w",stdout);

  
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=n;i++){
        cin>>b[i];
        lcm*=b[i];
    }
    for(int i=1;i<=n;i++){
        a[i]=(a[i]%b[i]+b[i])%b[i];
    }
    cout<<crt();

    return 0;
}

C

题意

P2398加强版。

思路

此题与P2398只相差一点,即求和时求的是 $\sum_{i=1}^n \sum_{j=1}^n \gcd(i,j)^2$。

因为P2398中,我们钦定 $\gcd=i$,所以此题仅需将 $i$ 更改为 $i^2$。

代码

void Pr(){
    phi[1]=1;
    for(int i=2;i<=n;i++){
        if(st[i]==0){
            cnt++;
            prime[cnt]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=cnt&&prime[j]*i<=n;j++){
            st[prime[j]*i]=1;
            if(i%prime[j]==0) {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            else phi[i*prime[j]]=phi[i]*phi[prime[j]];
        }
    }
}

signed main(){
\/\/   freopen("inq.in","r",stdin);
\/\/   freopen("inq.out","w",stdout);

    cin>>n;
    
    Pr();
    for(int i=1;i<=n;i++){
        s[i]=(s[i-1]+phi[i])%mod;
        \/\/cout<<s[i]<<' ';
    }
    \/\/Endl
    int ans=0;
    for(int i=1;i<=n;i++){

        ans+=(2*s[n\/i]-1)*i*i%mod;
    }
    cout<<ans%mod;
    return 0;
\/\/    fclose(stdin);
\/\/    fclose(stdout);
}

D

题意

P9007

E

题意

CF988D

思路

不难发现,选出的集合的大小不会大于3。

证:

当 $x=1$ 时,答案显然。

当 $x=2$ 时,答案显然。

当 $x=3$ 时,设三个数分别为 $x1$,$x2$,$x3$。

设 $x1-x2=2^{k1}$,$x2-x3=2^{k2}$,则$x1-x3=2^{k1}+2^{k2}$。

显然,当 $k1=k2$ 时,$2^{k1}+2^{k2}=2^{k3}$。

当 $x=4$ 时,设四个数分别为 $x1$,$x2$,$x3$,$x4$。

设 $x1-x2=2^{k1}$,$x2-x3=2^{k2}$,$x3-x4=2^{k3}$。

根据上述结论不难发现,$k1=k2=k3$。

那么,$x1-x4=3*2^{k1}$,显然不是 $2^k$。

当 $x>4$ 时,该集合定有一个子集长度为4。

根据上述结论,当$x \le 3$ 时,集合可能符合要求。

可以分讨+二分。

具体见代码。

代码

signed main(){
\/\/   freopen("inq.in","r",stdin);
\/\/   freopen("inq.out","w",stdout);

    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+1+n);
    for(int i=2;i<n;i++){\/\/枚举第二个数
        for(int j=0;j<=30;j++){
            int l=lower_bound(a+1,a+i,a[i]-(1<<j))-a;\/\/二分查找
            int r=lower_bound(a+i+1,a+1+n,a[i]+(1<<j))-a;
            if(a[l]+(1<<j)==a[i]&&a[i]+(1<<j)==a[r]){\/\/如果查找出来的数符合要求
                cout<<3<<endl;
                cout<<a[l]<<' '<<a[i]<<' '<<a[r];
                return 0;
            }
        }
    }
    for(int i=2;i<=n;i++){\/\/同上
        for(int j=0;j<=30;j++){
            int l=lower_bound(a+1,a+i,a[i]-(1<<j))-a;

            if(a[l]+(1<<j)==a[i]){
                cout<<2<<endl;
                cout<<a[l]<<' '<<a[i];
                return 0;
            }
        }
    }
    cout<<1<<endl<<a[1];
    return 0;
\/\/    fclose(stdin);
\/\/    fclose(stdout);
}

F

题意

用 $n$ 张纸币(1,5,10,50)能组成多少种不同的面值和?

$n\le 1e9$

思路

纯诈骗。

先DFS出 $n\le11$ 的情况,$n>11$ 时,进行差分,不难发现差分均为49.

代码


signed main(){
\/\/   freopen("inq.in","r",stdin);
\/\/   freopen("inq.out","w",stdout);
    cin>>n;
    if(n<=11) cout<<mny[n];\/\/自己算
    else  cout<<mny[11]+49*(n-11);
    return 0;
\/\/    fclose(stdin);
\/\/    fclose(stdout);
}

G

题意

AGC001C

思路

对 $k$ 进行分讨。

当 $k$ 为偶数,则枚举直径的中间点,以该点为起点查找其他点与该点距离 $\le k/2$ 的点。

当 $k$ 为奇数,则枚举直径的中间边,以该边的两个端点分别为起点查找其他点与该点距离 $\le k/2$ 的点。

代码

void dfs(int x,int len,int fa){
    vis[x]=1;
    cnt++;
    if(len==0) return ;
    for(int i=0;i<ve[x].size();i++){
        int v=ve[x][i];
        if(v==fa) continue;
        if(vis[v]) continue;
        dfs(v,len-1,x);
    }
}
signed main(){
\/\/   freopen("inq.in","r",stdin);
\/\/   freopen("inq.out","w",stdout);
    
    int maxx=0;
    cin>>n>>len;
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        ve[u].push_back(v);
        ve[v].push_back(u);
    }
    if(len%2==0){
        for(int i=1;i<=n;i++){
            memset(vis,0,sizeof(vis));
            cnt=0;
            dfs(i,len\/2,0);
            maxx=max(maxx,cnt);
        }
    }
    else{
        for(int i=1;i<=n;i++){
            for(int j=0;j<ve[i].size();j++){
                memset(vis,0,sizeof(vis));
                int v=ve[i][j];
                cnt=0;
                dfs(i,len\/2,v);
                dfs(v,len\/2,i);
                maxx=max(maxx,cnt);
            }
        }
    }
    cout<<maxx;
    return 0;
\/\/    fclose(stdin);
\/\/    fclose(stdout);
}

8.7 mx模拟赛记录

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-08-07 21:12:13

A

题意

给两个长度相等的字符串 $A$,$B$,每次可以在中任选一个字符并移到串的开头。求将 $A$ 变成 $B$ 的最小操作次数(无解输出 $-1$)。

思路

比较一眼。

不难发现,当且仅当两字符串的字母出现的次数不一样时,无解。

可以使用双指针,找到 $A$ 和 $B$ 的最长后缀(B必须连续,A不做要求),答案即为字符串的长度减去最长后缀。

代码

signed main(){
    freopen("move.in","r",stdin);
    freopen("move.out","w",stdout);
    cin>>a>>b;
    for(int i=0;i<a.size();i++){
        ta[a[i]-'A']++;
        tb[b[i]-'A']++;
    }
    for(int i=0;i<=27;i++){
        if(ta[i]!=tb[i]){
            cout<<-1;
            return 0;
        }
    }
    int j=b.size()-1;
    int cnt=0;
    for(int i=a.size()-1;i>=0;i--) if(a[i]==b[j]){
            j--;
            cnt++;
    }
    cout<<a.size()-cnt;
    return 0;
}
\/*
 0 7 4
 1 1 5 6 7 9 9
 *\/

B

题意

此处略

思路

40pts 是比较好拿的,二维前缀和直接判断即可。

不难发现, 以 $(x1,y1)$ 为左上角,$(x2,y2)$ 为右下角的子矩阵 $c$ 中 $1$ 的个数,就是 $a\left [ x1,\dots,x2\right ]$ 中 $1$ 的个数 乘上 $b\left [ y1,\dots,y2\right ]$ 中 $1$ 的个数。

枚举 $x \left (x \mid k\right )$,考虑 $a$ 有多少个区间和为 $x$,$b$ 有多少个区间和为 $\frac{k}{x}$,相乘即可。

可以记录 $a,b$ 序列每个0区间的长度,运用乘法原理求出 $a,b$ 序列中 $1$ 的个数为 $i$ 的区间个数。

细节蛮多的。

代码

signed main(){
    
   freopen("rain.in","r",stdin);
   freopen("rain.out","w",stdout);
    
    cin>>n>>m>>k;
    int cnta=0,sum=0;
    int la=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(a[i]){
            al[++cnta]=i-la;
            la=i;
        }
    }
    al[++cnta]=n-la+1;
\/\/    cout<<cnta<<endl;
\/\/    for(int i=1;i<=cnta;i++) cout<<al[i]<<' ';
    sum=0;
    int cntb=0,lb=0;
    for(int i=1;i<=m;i++){
        cin>>b[i];
        if(b[i]){
            bl[++cntb]=i-lb;
            lb=i;
        }
    }
    bl[++cntb]=m-lb+1;
\/\/    Endl
\/\/    cout<<cntb<<endl;
\/\/    for(int i=1;i<=cntb;i++) cout<<bl[i]<<' ';
    int cnt=0;
    for(int i=1;i<=k\/i;i++){
        if(k%i==0){
            d[++cnt]=i;
            if(k\/i!=i){
                d[++cnt]=k\/i;
            }
           
        }
    }
\/\/    for(int i=1;i<=cnt;i++){
\/\/        cout<<d[i]<<' ';
\/\/    }
\/\/    Endl
    for(int i=1;i<=cnt;i++){
        int x=d[i];
        for(int j=1;j+x<=cnta;j++) suma[x]=(suma[x]+al[j]*al[j+x]%mod)%mod;
        for(int j=1;j+x<=cntb;j++) sumb[x]=(sumb[x]+bl[j]*bl[j+x]%mod)%mod;
    }
    int res=0;
    for(int i=1;i<=cnt;i++){
        int x=d[i],y=k\/x;
        res=(res+suma[x]*sumb[y])%mod;
    }
    cout<<res%mod;
    
    return 0;
}

P6883 Kroničan 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-08-12 20:51:23

题意

P6883

思路

观察数据范围,考虑状压DP。

设 $dp_i$ 为状态为 $i$ 时,消耗的代价总和的最小值。当 $i$ 的第 $j$ 位为 $0$ 时,则第 $j$ 个杯子有水,反之没水。

初始化,$dp_0=0$。显然,当所有杯子都有水时,不需要操作,所以代价为 $0$。

不难推出状态转移方程: $$dp_i=\min(dp_i,dp_{i\oplus(1<<j)+c_{j+1,k+1}}) $$

此时,$i$ 的第 $j$ 位为 $1$,第 $k$ 位为 $0$。

此时做的操作为将第 $k$ 个杯子的水倒进第 $j$ 个杯子里。

其中,$i\oplus(1<<j)$ 时将 $i$ 的第 $j+1$ 位变成 $0$,既然倒水了,就要加上代价。

最后,我们枚举所有的状态,当有水的杯子数($0$ 的数量)比 $k$ 小,记录并更新最小值,输出最小值即可。

代码

signed main(){
    
  freopen("b.in","r",stdin);
  freopen("b.out","w",stdout);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++) cin>>c[i][j];
    }
    memset(dp,0x3f,sizeof(dp));
    dp[0]=0;
    for(int i=1;i<(1<<n);i++){
        for(int j=0;j<n;j++){
            if(i&(1<<j)){
                for(int k=0;k<n;k++){
                    if(!(i&(1<<k))){
                        dp[i]=min(dp[i],dp[i^(1<<j)]+c[j+1][k+1]);
                    }
                }
            }
        }
    }
    for(int i=0;i<(1<<n);i++){
        int cnt=0;
        for(int j=0;j<n;j++){
            if((i&(1<<j))==0) cnt++;
        }
       \/\/ cout<<i<<' '<<dp[i]<<' '<< cnt<<'\n';
        if(cnt<=m) minn=min(minn,dp[i]);
    }
    cout<<minn;
    
    return 0;
}

CF1883E

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-09-07 14:12:43

思路

首先,贪心不难发现 $a$ 数组的值改动的越少越好。

不难想到暴力,将 $a_i \times 2$ 直到 $a_i > a_{i-1}$,但是复杂度不允许。

不妨优化,利用前缀和思想:如果后面的数比当前小,则一定会有重复的步骤在当前的数已经计算过。

运用前缀和的前提: $$\forall a_i \Rightarrow \frac{a_i \times 2^x}{a_{i-1} \times 2^x}=\frac{a_i}{a_{i-1}}$$

令 $t_i$ 为 $a_i$ 需要乘二的次数。

则 $t_i$ 为 $a_i$ 与 $a_{i-1}$ 需要乘二的个数加上 $t_{i-1}$。

结果为 $\sum ^n_ {i=1} t_i$。

注意一点,如果 $t_i<0$,则 $t_i=0$ 。

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
#include<iomanip>
#include<algorithm>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<set>


using namespace std;

#define int long long
#define ll long long
#define Endl cout<<endl;
#define ENdl Endl
#define xy cout<<"xy";
#define yx cout<<"yx";
#define pii pair<int,int>
#define lowbit(x) x&(-x)
#define fi first
#define se second
#define fst ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);

const int N=5e6+10;\/\/注意修改
const int mod=1e9+7;
const int Max=0x3f3f3f3f3f;

int a[N];

void solve(){
    int n;
    cin>>n;
    int cnt=0,sum=0,ans=0;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=2;i<=n;i++){
        cnt=0;
        int l=a[i-1],r=a[i];
        \/\/if(l<=r) continue;
        while(l<r) l*=2,cnt--;\/\/少乘
        while(l>r) r*=2,cnt++;\/\/多乘
        sum+=cnt;\/\/最后乘多少
        if(sum<0) sum=0;\/\/如果是负数,那就不用管
        ans+=sum;
    }

    cout<<ans<<endl;

}


\/*
 *\/
signed main(){
    
\/\/   freopen("a.in","r",stdin);
\/\/   freopen("a.out","w",stdout);
    int T;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}



\/*
 1
 5 3
 2 4 5

 *\/

Tian Ji -- The Horse Racing 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-03-30 14:04:20

题意

就是田忌赛马,赢一场比赛得+200银元,输了得-200银元,平局不加不减,求田忌最多能得到几个银元。

思路

这个题很显然是贪心,我们主要思想就是$以小换大$。 将齐王最快的马用我们最慢的马去换掉,这是最好的。

如果田忌最快的马比齐王最快的马快,就直接去比;

如果田忌最慢的马比齐王最慢的马快,也直接去比;

如果田忌最快的马比齐王最快的马慢,就用田忌最慢的马去比;

如果田忌最慢的马比齐王最慢的马慢,就和齐王最快的马去比;

我们可以用类似指针的方法做,$la$ 是田忌最慢的马的下标,$lb$ 是齐王最慢的马的下标,$ra$ 是田忌最快的马的下标,$rb$ ,是齐王最快的马的下标。

当然,我们必须在前面sort一下,从小到大排一遍序。

代码

#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int N=1e3+5;
int a[N],b[N];
signed main(){
    int n;
    while(cin>>n){
        if(n==0) return 0;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int j=1;j<=n;j++) cin>>b[j];
        sort(a+1,a+1+n);
        sort(b+1,b+1+n);
        int la,lb,ra,rb;
        la=lb=1;\/\/最慢的马
        ra=rb=n;\/\/最快的马
        int res=0;
        for(int i=1;i<=n;i++){
            if(a[ra]>b[rb]){
                res+=200;
                ra--;
                rb--;
            }
            else if(a[ra]<b[rb]){
                res-=200;
                la++;
                rb--;
            }
            else if(a[la]>b[lb]){
                res+=200;
                la++;
                lb++;
            }
            else{
                if(a[la]<b[rb]){
                    res-=200;
                    la++;
                    rb--;
                }
            }
        }
        cout<<res<<endl;
    }
}

这道题居然只有一个测试点!!

题解:CF7E Defining Macros

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-04-06 10:15:55

思路

众所周知,乘除法的运算优先级大于加减法的运算优先级,有括号先算括号里面的。

输入

首先输入,注意这句话:

The input lines may contain any number of spaces anywhere, providing these spaces do not break the word "define" or the names of constructions and variables. In particular, there can be any number of spaces before and after the "#" symbol.

意思就是:

在 # 前后可能出现任意多个空格。

我们先输入名字和内容,内容要去空格处理后才可用。

判断

我们发现有四种可能:

  • a.内容完全正确,类似 ( $\text x$ + $\text y$ ),( $\texttt ...$ )。

  • b.内容可能正确,类似 $\text x$ + $\text y$,$\text x$ - $\text y$,( $\texttt ...$ ) - $\text y$。

  • c.内容可能正确,类似 $\text x$ * $\text y$,$\text x$ / $\text y$,( $\texttt ...$ ) / $\text y$。

  • d.内容完全错误,类似 !@#$%^

我们从后往前判断(从前往后会在第六个 WA),将整个式子分成两部分。

如果两部分都是 d,那么整个式子也是 d;

如果分隔符是 $+$,那么整个式子是 b;

如果分隔符是 $-$,并且后面的式子为 b,那么整个式子为 d,否则为 b。

如果分隔符是 $*$,如果前后两个式子有一个是 b,那么整个式子为 d。否则为 c。

如果分隔符是 $/$,如果前后两个式子有一个是 b或者后面的式子为 c,那么整个式子为 d,否则是 c。

如果这个式子第一个字符为 (,或第四个字符为 ),我们只需要判断括号里面的是不是对的。

如果以上都不是,那么这个式子只能是一串字母。我们用 map 存一下我们输入的宏定义的状态 (a,b,c,d),判断这串字母是不是宏定义,如果是宏定义 那么判断这个宏定义是不是对的;如果不是宏定义,就直接正确。

整体

我们只需要把最后输入的字符串判断一下是不是对的即可。

代码

#include<iostream>
#include<map>
#include<string>
using namespace std;
map<string,int> mp;
\/*
 level:
 1: 左右全是括号,完全正确
 2: x+y x-y
 3: x*y,x\/y
 4: !@#$%^ 完全错误
 *\/
int check(string s,int l,int r){\/\/递归进行,s是字符串,l和r是左右下标
    int f=0;
    int kuohao=0;
    int l1,l2;
    for(int i=r;i>=l;i--){
        if(s[i]=='(') kuohao++;
        else if(s[i]==')') kuohao--;
        else if((kuohao==0&&f==0)&&(s[i]=='*'||s[i]=='\/')){
            f=i;
        }
        else if((kuohao==0&&(s[i]=='+'||s[i]=='-'))){
            l1=check(s,l,i-1);
            l2=check(s,i+1,r);
            \/\/cout<<l1<<" "<<l2<<endl;
            if(l1==4||l2==4) return 4;
            if(s[i]=='+'){
                return 2;
            }
            if(s[i]=='-'){
                if(l2==2) return 4;
                else return 2;
            }
        }
        
    }
    if(f!=0){
        l1=check(s,l,f-1);
        l2=check(s,f+1,r);
        if(l1==4||l2==4) return 4;
        if(s[f]=='*'){
            if(l1==2||l2==2) return 4;
            else return 3;
        }
        if(s[f]=='\/'){
            if(l2==2||l1==2||l2==3) return 4;
            else return 3;
        }
    }
    else if(s[l]=='('&&s[r]==')'){
        if(check(s,l+1,r-1)==4){
            return 4;
        }
        return 1;
    }
    else{
        string ss;
        ss="";
        for(int j=l;j<=r;j++){
            ss+=s[j];
        }
        if(mp[ss]!=0){
            return mp[ss];
        }
        return 1;
    }
    return 1;
}
signed main(){
    int T;
    cin>>T;
    for(int i=1;i<=T;i++){
        string t;
        cin>>t;
        if(t[t.size()-1]=='#') cin>>t;
        string na;
        cin>>na;
        string s3;\/\/去空格后的字符串
        string s2;\/\/去空格前的字符串
        getline(cin,s2);
        s3="";
        for(int j=0;j<s2.size();j++){
            if(s2[j]!=' '){
                s3+=s2[j];
            }
        }
        \/\/cout<<s3<<endl;
        mp[na]=check(s3,0,s3.size()-1);
        \/\/cout<<mp[s[i].na]<<endl;

    }
    string s2;
    getline(cin,s2);
    string code;
    for(int j=0;j<s2.size();j++){
        if(s2[j]!=' '){
            code+=s2[j];
        }
    }
    \/\/cout<<code<<endl;
    if(check(code,0,code.size()-1)!=4){
        cout<<"OK";
    }
    else{
        cout<<"Suspicious";
    }
    return 0;
}

ABC349E Weighted Tic-Tac-Toe

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-04-19 12:53:57

感谢@KSCD_

思路

我们发现数据范围非常小,可以用DFS直接解决。

显然,这道题需要回溯。我们设三个参数 $st$,$x$,$y$,分别代表涂色格子数,高桥的总分与青木的总分。

首先我们需要判断前一个操作者有没有连成三个子,如果连成了,返回假。

如果 $st==9$,就根据总分大小来分胜负。

我们遍历每一个格子,如果这个格子还没有涂过色,就将其涂上现在操作者的颜色。后面dfs即可,注意回溯。

代码

#include<iostream>
using namespace std;
#define int long long
const int N=4;
int a[N][N];
int v[N][N];
int dfs(int st,int x,int y){\/\/x:Takahshi    y:Aoki   的总分
    int xs;\/\/现手
    xs=st%2+1;\/\/如果是1,就是Takashi
    int hs;
    hs=(st+1)%2+1;
    if(v[1][1]==hs&&v[1][2]==hs&&v[1][3]==hs) return 0;
    if(v[2][1]==hs&&v[2][2]==hs&&v[2][3]==hs) return 0;
    if(v[3][1]==hs&&v[3][2]==hs&&v[3][3]==hs) return 0;
    if(v[1][1]==hs&&v[2][2]==hs&&v[3][3]==hs) return 0;
    if(v[1][3]==hs&&v[2][2]==hs&&v[3][1]==hs) return 0;
    if(v[1][1]==hs&&v[2][1]==hs&&v[3][1]==hs) return 0;
    if(v[1][2]==hs&&v[2][2]==hs&&v[3][2]==hs) return 0;
    if(v[1][3]==hs&&v[2][3]==hs&&v[3][3]==hs) return 0;
    \/\/cout<<st;
    if(st==9){\/\/如果格子全部被填满
        if(y<x){
            return 0;
        }
        return 1;
    }
 
    int f=0;
    for(int i=1;i<=3;i++){
        for(int j=1;j<=3;j++){
            if(v[i][j]==0){
                v[i][j]=xs;
                if(xs==1){
                    f=dfs(st+1,x+a[i][j],y);
                }
                else{
                    f=dfs(st+1,x,y+a[i][j]);
                }
                v[i][j]=0;
                if(f) return 1;
            }
        }
    }
    return 0;
}
signed main(){
    for(int i=1;i<=3;i++) for(int j=1;j<=3;j++) cin>>a[i][j];
    if(dfs(0,0,0)) cout<<"Takahashi";
    else cout<<"Aoki";
}

P6480 [CRCI2006-2007] TETRIS 题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-04-20 14:56:26

一眼大模拟。

题意

不多说了

生动形象的题意

思路

题目中已经告诉了俄罗斯方块7种形状。想要让任何一列只有底部连续若干行有方块,我们需要分类讨论。

1

分两种情况,横放和竖放。

竖放

竖放在哪一列放都行。

横放

横放需要有四个连续相等的高度。

    if(m==1){
        for(int i=1;i<=n-3;i++){
            if(a[i]==a[i+1]&&a[i+1]==a[i+2]&&a[i+2]==a[i+3]){
                cnt++;
            }
            
        }
        cout<<cnt+n;
    }

2

我们发现横放和竖放都是一种图形。

横放+竖放

需要两个连续相等的高度

    if(m==2){
        for(int i=1;i<n;i++){
            if(a[i]==a[i+1]){
                cnt++;
            }
           
        }
        cout<<cnt;
    }

3

分横放和竖放。

横放

横放需要前两个高度相等,第三个比第二个高 $1$。

竖放

竖放第一个比第二个高 $1$。

    if(m==3){
        for(int i=1;i<=n-2;i++){
            if(a[i]==a[i+1]&&a[i+2]-a[i+1]==1){
                cnt++;
            }
            
        }
        for(int i=1;i<=n-1;i++){
            if(a[i]-a[i+1]==1){
                cnt++;
            }
            
        }
        cout<<cnt;
    }

4

3 几乎一样,不过方向反了。

    if(m==4){
        for(int i=1;i<=n-2;i++){
            if(a[i+2]==a[i+1]&&a[i]-a[i+1]==1){
                cnt++;
            }
            
        }
        for(int i=1;i<=n-1;i++){
            if(a[i+1]-a[i]==1){
                cnt++;
            }
            
        }
        cout<<cnt;
    }

5

分正放,竖放,倒放,

正放

正放需要三个相同的高度。

竖放

竖放分两种情况

顺时针90°

第二个比第一个高 $1$。

逆时针90°

第一个比第二个高 $1$。

倒放

第二个比第一个和第三个少 $1$。

    if(m==5){
        for(int i=1;i<=n-2;i++){\/\/正放
            if(a[i]==a[i+1]&&a[i+1]==a[i+2]){
                cnt++;
            }
            
        }
        for(int i=1;i<=n-1;i++){\/\/逆时针90
            if(a[i]-a[i+1]==1){
                cnt++;
            }
            
        }
        for(int i=1;i<=n-1;i++){\/\/顺时针90
            if(a[i]-a[i+1]==-1){
                cnt++;
            }
            
        }
        for(int i=1;i<=n-2;i++){\/\/倒放
            if(a[i]-a[i+1]==1&&a[i+2]-a[i+1]==1){
                cnt++;
            }
            
        }
        cout<<cnt;
    }

6

分正放,竖放,倒放,

正放

正放需要三个相同的高度。

竖放

竖放分两种情况

顺时针90°

需要两个相同的高度

逆时针90°

第一个比第二个高 $2$。

倒放

第一个比第二个和第三个少 $1$。

   if(m==6){
        for(int i=1;i<=n-2;i++){
            if(a[i]==a[i+1]&&a[i+1]==a[i+2]){
                cnt++;
            }
            
        }
        for(int i=1;i<=n-1;i++){
            if(a[i]-a[i+1]==2){
                cnt++;
            }
            
        }
        for(int i=1;i<n;i++){
            if(a[i]==a[i+1]){
                cnt++;
            }
           
        }
        for(int i=1;i<=n-2;i++){
            if(a[i]-a[i+1]==-1&&a[i+2]-a[i+1]==0){
                cnt++;
            }
            
        }
        cout<<cnt;
    }

7

分正放,竖放,倒放,

正放

正放需要三个相同的高度。

竖放

竖放分两种情况

顺时针90°

第二个比第一个高 $2$。

逆时针90°

需要两个相同的高度。

倒放

第三个比一个和第二个少 $1$。

 if(m==7){
        for(int i=1;i<=n-2;i++){
            if(a[i]==a[i+1]&&a[i+1]==a[i+2]){
                cnt++;
            }
            
        }
        for(int i=1;i<=n-1;i++){
            if(a[i]-a[i+1]==-2){
                cnt++;
            }
            
        }
        for(int i=1;i<n;i++){
            if(a[i]==a[i+1]){
                cnt++;
            }
           
        }
        for(int i=1;i<=n-2;i++){
            if(a[i]-a[i+1]==0&&a[i+2]-a[i+1]==-1){
                cnt++;
            }
            
        }
        cout<<cnt;
    }

创作不易,请三连(关注作者+点赞+收藏),感激不尽。

5.4 杂记

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-05-04 09:42:27

同余:a和b除以p的余数相同,记作 $a\equiv b\pmod{p}$。


模运算:

  1. $(a+b) \mod p=((a\mod p) + (b \mod p)) \mod p$

  2. $a \times b \mod p =(a \mod p) \times (b \mod p) \mod p$

  3. 若 $a\equiv b\pmod{p}$,$b\equiv c\pmod{p}$,则$a\equiv c\pmod{p}$。


费马小定理:若p为质数,则$a^p \equiv a \pmod{p}$

当a不是p的倍数时,$a^{p-1} \equiv 1 \pmod{p}$


埃氏筛复杂度:$O(n\log \log (n))$


Pollard_Rho 算法(未写完)

生成一个随机序列a,若 $\gcd(\left\ ert a_i-a_{i-1}\right\ ert)$

7.4 号爸 斯特林数笔记

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-07-04 20:42:27

斯特林数

第一类斯特林数

$ \begin{bmatrix}n\\m\end{bmatrix} $(或 $s(n,m)$ )

表示将 $n$ 个不同元素构成 $m$ 个圆排列的数目。

性质

$ \begin{bmatrix}n\\end{bmatrix} $ = 0;

将 0 个数放到 0 个圆排列中,方案数为 1 


$\begin{bmatrix}n\n\end{bmatrix}$= 1

将$n$个数放到 $?$ 个圆排列中,每一个数占用一个圆,方案数为1


$\begin{bmatrix}n\\end{bmatrix}$= $(n-1)!$

将 $?$ 个数放到 1 个圆排列中,就是 1 \~ $?$ 的圆排列的方案数,$(?−1)!$。


$\begin{bmatrix} n\n-2 \end{bmatrix} = C^2_n$

将n个数放到 $?−1$ 个圆排列中的方案数。即有一个圆排列是两个数,其余均为 1 个数的方案数,也就是从 $?$ 个数里选择两个数,剩余的 $?−2$ 个数自动每一个数归为一个圆排列,所以总方案数为 $C_?^2$。


我们考虑如何求解第一类斯特林数,一般首先考虑递推。

考虑这样一个问题:将 1 \~ $?$ 的一个排列,划分为 $k$ 个圆排列,一共有多少种方案。

我们使用DP的分析方法,分析第 $?$ 个数 $?$,有哪些放置方法。

发现一共有两种放置方法:$?$ 放到一个新的圆中,或者 ? 放到已有的圆中。

  • $?$ 放到一个新的圆中

此时就意味着前 $?−1$ 个数放到 $?−1$ 个圆中,$?$ 放到自己的圆中,也就是第 $?$ 个圆,此时的方案数等于 $\begin{bmatrix} ?−1\?−1 \end{bmatrix}$。

  • $?$ 放到已有的圆中

即前 $?−1$ 个数放到 $?$ 个圆里,第 $?$ 个数放到前面 $?$ 个圆里,那么对于每一个圆中,一共有 $?−1$ 个数所以对于 ? 来说,一共有 ?−1 中放置方案。故此时的方案数等于 $(?−1) × $ $\begin{bmatrix} ?−1\? \end{bmatrix}$。

根据加法原理,我们可以得到递推式:

 $\begin{bmatrix} ?\? \end{bmatrix}$ $=$ $\begin{bmatrix} ?−1\?−1 \end{bmatrix}$ $+$ $(?−1)$ $×$ $\begin{bmatrix} ?−1\? \end{bmatrix}$

第二类斯特林数

第二类斯特林数实际上是集合的一个拆分,表示将 ? 个不同的元素拆分成 $?$ 个集合间有序(可以理解为集合上有编号且集合不能为空)的方案数,记为  $\begin{Bmatrix}n\\m \end{Bmatrix}$ (或$?(?,?)$ )。

性质

$\begin{Bmatrix}n\ \end{Bmatrix}=0$

$\begin{Bmatrix}n\ \end{Bmatrix}=1$

$\begin{Bmatrix}n\ \end{Bmatrix}=2^{n-1}-1$

$\begin{Bmatrix}n\{n-1} \end{Bmatrix}=C^2_n$

$\begin{Bmatrix}n\n \end{Bmatrix}=1$

第二类Stirling数要求盒子是无区别的,所以可以得到其方案数公式:

假设要把 ? 个元素分成 ? 个集合,我们来分析第 $?$ 个数 $?$,有哪些放置方法。

发现还是一共有两种放置方法:$?$ 放到一个新的集合中,或者 $?$ 放到已有的集合中。

  • $?$ 放到一个新的集合中

此时就意味着前 ?−1 个数放到 ?−1 个集合中,? 放到自己的集合中,也就是第 ? 个集合,此时的方案数等于 $\begin{Bmatrix}n-1\\k-1 \end{Bmatrix}=1$。

  • $?$ 放到已有的集合中

即前 ?−1 个数放到 ? 个集合里,第 ? 个数放到前面 ? 个集合里,那么对于每一个集合中,由于内部是无序的,而集合间是有序的,所以相当于将 ? 放到 ? 个箱子里,有 ? 种选择。故此时的方案数等于 $?×\begin{Bmatrix}n-1\\k \end{Bmatrix}=1$。

故可以得到第二类斯特林数的递推公式:

$\begin{Bmatrix}n\\k \end{Bmatrix}=\begin{Bmatrix}n-1\\k-1 \end{Bmatrix}+ ? ×\begin{Bmatrix}n-1\\k \end{Bmatrix}=1$`

证毕

——S08577

共 53 篇博客