2021-11-20(每周总结)

142 篇文章 1 订阅
92 篇文章 0 订阅

         这周打了几场比赛,补了补题,到现在来说自己能做的还是只有思维题,算法题虽然学了但现在也只是能应付个模板题,补完题又去学了数论的一些知识,鸽巢原理,RMQ算法和容斥原理,这两个原理看起来挺简单的,但一碰见有关的题难度却在意料之外,看来熟练运用是有难度的,近期因为是在学数学主要是在vjudge做题,洛谷的题想等到数论看一遍之后再去刷

牛客 Mio visits ACGN Exhibition

        看数据范围就知道搜索必不可行,看的题解用的是dp,dp[i][j][x]表示走到(i,j)时,拥有x个0,i+j-1-x个1,可以用滚动数组优化掉i这一维,则有状态方程

if(mp[i][j]) dp[j][x]=(dp[j-1][x]+dp[j][x])%mod;

else {if(x) dp[j][x]=(dp[j-1][x-1]+dp[j][x-1])%mod;

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=998244353;
const int inf=0x3f3f3f3f;
ll n,m,p,q,dp[505][1100],mp[1100][1100];
int main(){
    //freopen("in.txt","r",stdin);
    scanf("%lld%lld%lld%lld",&n,&m,&p,&q);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        scanf("%lld",&mp[i][j]);
    (!mp[1][1])?dp[1][1]=1:dp[1][0]=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(i==1&&j==1) continue;
            for(int x=i+j-1;x>=0;x--){
                if(mp[i][j]) dp[j][x]=(dp[j-1][x]+dp[j][x])%mod;
                else {if(x) dp[j][x]=(dp[j-1][x-1]+dp[j][x-1])%mod;}
            }
            if(!mp[i][j]) dp[j][0]=0;//怕重复计算
//当a[i][j]=0时,由dp[j][0]转移到f[j][1]已经将a[i][j]=0的贡献加进去了,若不将dp[j][0]清空,
//转移到第i+1行,dp[j][0]的会被重复加
        }
    }
    ll ans=0;
    for(int i=p;i<=n+m-1-q;i++)
        ans=(ans+dp[m][i])%mod;
        printf("%d\n",ans);
    return 0;
}

poj 2356

可以得出结论,n个数一定会有若干个连续数的和是n的倍数

假设有n个数,设sum[i]为前i个数的和,会有两个情况(1):至少有1个sum[i]是n的倍数

(2):没有一个sum[i]是n的倍数

如果是情况1,那么结论成立;如果是情况2,那么我们可以想一下,每个sum[i]都有sum[i]%n!=0,就说明取余的结果有n种,但取余的范围是0-n-1,所以必有两个sum[i],sum[j],他们取余n结果是相同的,那就可以说明他们的差一定会是n的倍数

Find a multiple (POJ-2356)(抽屉原理)_菜鸡的博客-CSDN博客

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#define ll long long
using namespace std;
const int maxx=10010;
const int MOD=998244353;
const int inf=0x3f3f3f3f;
int a[maxx];
int sum[maxx],mod[maxx];
int main(){
    //freopen("in.txt","r",stdin);
    int n;
    while(~scanf("%d",&n)){
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            sum[i]=a[i]+sum[i-1];
        }
        for(int i=1;i<=n;i++){
            if(sum[i]%n==0){
                printf("%d\n",i);
                for(int j=1;j<=i;j++)
                    printf("%d\n",a[j]);
                break;
            }
            if(mod[sum[i]%n]!=0){//说明有和他相同取余的i
                printf("%d\n",i-mod[sum[i]%n]);
                for(int j=mod[sum[i]%n]+1;j<=i;j++)
                    printf("%d\n",a[j]);
                break;
            }
            mod[sum[i]%n]=i;//记录取余情况
        }
    }
    return 0;
}

hdu 3183

        关于RMQ算法和鸽巢原理的题,但我并没有发现哪里用到了鸽巢原理。。。难不成是删数的时候用到?可能是变了一下我就不懂了,可能是这个地方:删m个数就代表保留n-m个数,要使第一位最小,那么只能从1--m+1个数中取,假设取到了第k个,那么第二个数就是k+1到m+2中取,以此类推,最后判断前导0;

为此学了下RMQ算法。。。

RMQ算法讲解_不期而遇-CSDN博客_rmq

HDU-3183——A Magic Lamp(RMQ问题+鸽巢原理)_shengtao96的专栏-CSDN博客

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#define ll long long
using namespace std;
const int inf=0x3f3f3f3f;
ll mod=1e9+7;
char str[1005],ans[1005];
int dp[1005][10];
int minn(int i,int j){
    return str[i]<=str[j]?i:j;
}
void rmq(int n){
    memset(dp,inf,sizeof(dp));
    for(int i=0;i<n;i++)
        dp[i][0]=i;//初始化,表示第i位往后2^j个数的最小值
    for(int j=1;j<10;j++)
        for(int i=0;i<n&&i+(1<<j)-1<n;i++)
        dp[i][j]=minn(dp[i][j-1],dp[i+(1<<j-1)][j-1]);//每次都分成两半查找
}
int query(int l,int r){//查找整个区间的最小值
    int k=log2(r-l+1);
    return minn(dp[l][k],dp[r-(1<<k)+1][k]);
}
int main(){
    //freopen("in.txt","r",stdin);
    int m;
    while(~scanf("%s %d",str,&m)){
        int n=strlen(str);
        rmq(n);
        int ld=-1,M=m;
        int len=n-m;
        for(int i=0;i<len;i++,M++){
            ld=query(ld+1,M);
            ans[i]=str[ld];
        }
        int kk=0;
        while(ans[kk]=='0'&&kk<len)kk++;
        if(kk==len){
            printf("0\n");
            continue;
        }
        for(;kk<len;kk++)
            printf("%c",ans[kk]);
        printf("\n");
    }
    return 0;
}

codeforces C Array Elimination

        关于位运算的题目

Codeforces-1601 A: Array Elimination_Sherlock_Holmewei的博客-CSDN博客

#include<bits/stdc++.h>
using namespace std;
int t,n,a[200005],bit[33];
int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d",&t);
    while(t--){
        for(int i=0;i<32;i++) bit[i]=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            for(int j=0;j<32;j++){
                bit[j]+=(1&(a[i]>>j));
            }
        }
        int k=bit[0];
        for(int i=1;i<32;i++) k=__gcd(k,bit[i]);
        if(k==0){
            for(int i=1;i<=n;i++) cout<<i<<" ";
        }
        else{
            for(int i=1;i<=k;i++){
                if(k%i==0) cout<<i<<" ";
            }
        }
        cout<<endl;
    }
    return 0;
}

hdu 2841

求m个数中与i互质的个数,容斥原理的题目,虽然有公式:n/2+n/3+n/5-n/(2*3)-n/(2*5)-n/(3*5)+n/(2*3*5)(奇数加,偶数减),但还是不明白其中的道理,先写上之后再问问别人;

容斥原理模板(二进制表示)_MInNrz的Love&Share-CSDN博客

Visible Trees (HDU-2841)(容斥原理)_菜鸡的博客-CSDN博客 题解

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#define ll long long
using namespace std;
const int inf=0x3f3f3f3f;
int fac[100000];
int div(int n){//找n的质因子及其个数
    int cnt=0;
    for(int i=2;i*i<=n;i++){
        if(n%i==0){
            fac[cnt++]=i;
            while(n%i==0) n/=i;
        }
    }
    if(n>1) fac[cnt++]=n;
    return cnt;
}
int solve(int n,int cnt){
    int ans=0;
    for(int i=1;i<(1<<cnt);i++){//质因子集合子集的个数一共(2^cnt)-1个
        int ones=0,mul=1;       //不算空集
        for(int j=0;j<cnt;j++){
            if(i&(1<<j)){//如果i的第j位有1,说明j在这个子集内
                ones++;
                mul*=fac[j];
            }
        }//容斥原理公式是:n/2+n/3+n/5-n/(2*3)-n/(2*5)-n/(3*5)+n/(2*3*5)
        //说白了就是奇数就加,偶数就减
        if(ones&1) ans+=n/mul;
        else ans-=n/mul;
    }
    return n-ans;
}
int main(){
    //freopen("in.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        ll ans=0;
        cin>>n>>m;
        for(int i=1;i<=n;i++){
            int cnt=div(i);
            ans+=solve(m,cnt);//这里计算的是1到m这m个数中与i互质的个数
    //枚举行数,求出第i行的m个数中与i互质的个数,最后全加起来就是答案
        }
        cout<<ans<<endl;
    }
    return 0;
}

hdu 4135

容斥原理,找L/G质因子出现的次数然后乘以6,最后全部乘起来就是答案,但没大理解到底为什么要这样做

这篇讲的思路hdu4497 (唯一分解定理)_sole’s blog-CSDN博客

看的这篇的代码HDU 4497 GCD and LCM (数论&组合数学)_AC,∑ndless-CSDN博客

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#define ll long long
using namespace std;
const int inf=0x3f3f3f3f;

int main(){
    int t;
    //freopen("in.txt","r",stdin);
    ll m,n,ans,i,cnt;
    scanf("%d",&t);
    while(t--){
        scanf("%lld%lld",&m,&n);
    if(n%m) cout<<0<<endl;//n%m!=0直接不符合条件
    else{
        n/=m;
        ans=1;
        for(int i=2;i*i<=n;i+=2){
            if(n%i==0){
                cnt=0;
                while(n%i==0){
                    n/=i;
                    ++cnt;//统计该质因子出现的次数
                }
                ans*=6*cnt;//有6种组合情况所以×6
            }
            if(i==2)
                --i;//让后面的i都变为素数
        }
        if(n>1) ans*=6;
        printf("%lld\n",ans);
    }
    }
    return 0;
}

hdu 5155

依次求出至少有x列一个钻石也没有的情况,最后利用容斥原理做

HDU-5155-Harry And Magic Box(容斥)_娃娃酱斯密酱的博客-CSDN博客

#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iomanip>
#include<map>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<set>
#define ll long long
using namespace std;
const int inf=0x3f3f3f3f;
const int mod=1000000007;
ll c[55][55];
int n,m;
void init(){//求的是组合数
    c[0][0]=1;
    for(int i=1;i<=50;i++){
        c[i][0]=c[i][i]=1;
    }
    for(int i=2;i<=50;i++)
        for(int j=1;j<i;j++)
        c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
ll npow(ll a,int n){
    ll res=1;
    while(n){
        if(n&1) res=res*a%mod;
        a=a*a%mod;
        n>>=1;
    }
    return res;
}
int main(){
    //freopen("in.txt","r",stdin);
    init();
    while(~scanf("%d%d",&n,&m)){
        ll ans=0;
        for(int i=0;i<=m;i++){
            //至少有i列一个钻石也没有的情况
            ll t=c[m][i]*npow(npow(2,m-i)-1,n)%mod;
            if(i&1) ans=(ans-t+mod)%mod;
            else ans=(ans+t)%mod;
        }
        cout<<ans%mod<<endl;
    }
    return 0;
}

codeforces J Prime Game

普通的打表暴力必会超时,这是个偏思维的数学题,可以算出每个素数对整个序列的贡献度,最后全部加起来,题解讲的详细多了。。。

J Prime Game(数学,思维,拆分素因子)_Plus Ultra!-CSDN博客

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
int n;
int a[1000010];
vector<int>v[1000010];
int main(){
   // freopen("in.txt","r",stdin);
   
    for(int i=2;i<1000010;i++)
        v[i].push_back(0);
        scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        int m=a[i];
        for(int j=2;j*j<=m;j++){
            if(m%j==0){
                v[j].push_back(i);
                while(m%j==0) m/=j;
            }
        }
        if(m>1) v[m].push_back(i);
    }
    ll ans=0;
    for(ll i=2;i<1000010;i++)
        for(ll j=1;j<v[i].size();j++)
        ans+=(ll)(v[i][j]-v[i][j-1])*(n-v[i][j]+1);
        //v[i][j]代表第j个包含素数i的数的位置
    cout<<ans<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

killer_queen4804

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值