ICPC省赛

1. Mio visits ACGN Exhibition(线性dp+滚动数组)

题意:
输入一个二维01矩阵,求从左上角走到右下角满足如下条件的路径数:经过0的个数不小于p,经过1的个数不小于q

输入:
2 2 1 1  //n,m,p,q
0 0
1 1
输出:
2

输入:
3 3 2 0
0 0 1
0 0 1
1 0 0
输出:
6
#include<iostream>
#include<cstring>
using namespace std;

const int mod=998244353;
int arr[510][510];
int n,m,p,q;
int f[2][510][1010];//第一维滚动数组,四维优化三维,只考虑走0的情况,走1的情况相减即可得知

int main()
{
    cin>>n>>m>>p>>q;
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            scanf("%d",&arr[i][j]);
        }
    }

    if(arr[1][1]==0) f[1][1][1]=1;//初始化
    else f[1][1][0]=1;

    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            for(int k=0; k<=n+m-1; k++)
            {
                if(i==1&&j==1) continue;//这种情况已经初始化

                if(arr[i][j]==0)
                {
                    if(k!=0) f[i%2][j][k]=(f[(i-1)%2][j][k-1]+f[i%2][j-1][k-1])%mod;//k=0时,数组越界,这里是真坑
                    else f[i%2][j][k]=0;
                }

                else f[i%2][j][k]=(f[(i-1)%2][j][k]+f[i%2][j-1][k])%mod;
            }
        }
    }
    int cnt=0;
    for(int i=p; i<=n+m-1-q; i++)//0的个数要大于p,小于等于n+m-1-q
    {
        cnt=(cnt+f[n%2][m][i])%mod;
    }

    cout<<cnt<<endl;
    return 0;
}

2. Dyson Box(模拟)

题意:
有n个箱子,输入他们的坐标,这些箱子会受到两种力:向左和向下。因此他们会被推到最左边或最下边。每加入一个箱子,我们都要求当前所有箱子受到向左或者向下的力之后的周长
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

输入:
4
1 2
3 2
2 1
4 1
输出:
4 4
8 6
8 8
10 8
#include<iostream>
using namespace std;

int ver[200020];//x轴
int hor[200020];//y轴

int main()
{
    int n;
    cin>>n;
    int x1,y1;
    long long heng=0,shu=0;
    while(n--)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        ver[x]++;
        hor[y]++;
        x1=4;
        y1=4;
        if(ver[x]>1)
            x1-=2;
        if(hor[y]>1)
            y1-=2;
        if(ver[x-1]>=ver[x])
            x1-=2;
        if(ver[x+1]>=ver[x])
            x1-=2;
        if(hor[y+1]>=hor[y])
            y1-=2;
        if(hor[y-1]>=hor[y])
            y1-=2;
        heng+=x1;
        shu+=y1;
        printf("%d %d\n",heng,shu);
    }
    return 0;
}

3. Icebound and Sequence(快速幂+龟速乘)

在这里插入图片描述

输入:
2
2 3 100
511 4 520
输出:
14
184

对结果取余时,为了防止数爆longlong,通常在计算的过程中取余,然而过程取余会对结果相除造成影响(除非除数是模的因子)

#include<iostream>
using namespace std;

int t;

long long slow_mul(long long x,long long y,long long mod)
{
    long long ans=0;
    while(y)
    {
        if(y&1) ans=(ans+x)%mod;
        x=(x+x)%mod;
        y=y>>1;
    }
    return ans;
}

long long qmi(long long x,long long y,long long mod)
{
    long long cnt=1;
    while(y)
    {
        if(y&1) cnt=slow_mul(cnt,x,mod)%mod;
        x=slow_mul(x,x,mod)%mod;
        y=y>>1;
    }
    return cnt;
}

int main()
{
    cin>>t;
    while(t--)
    {
        int q,n,p;
        cin>>q>>n>>p;
        cout<<(qmi(q,n+1,(q-1)*p)-q)/(q-1)<<endl;//等比公式,注意取余的问题
    }
    return 0;
}

4. 舔狗(贪心)

在这里插入图片描述

输入:
10
3 1 8 6 10 1 4 1 6 1
输出:
0
#include<iostream>
#include<queue>
using namespace std;

int n;
int st[2000020],like[2000020];
int in[2000020];

struct node
{
    int u,v;
    bool friend operator<(node a,node b)//优先级队列如果插入的节点是结构体类型,则要在结构体中重载比较操作符函数。
    {
        return a.v>b.v;//<为从大到小排列,>为从小到大排列
    }
};

int solve()
{
    int ans=0;
    priority_queue<node> q;
    for(int i=1;i<=n;i++)
    {
        q.push({i,in[i]});
    }
    while(!q.empty())
    {
        node t=q.top();
        q.pop();
        if(st[t.u]||st[like[t.u]]) continue;//如果他或者他喜欢的人已经配对,就跳到下一次循环
        ans=ans+2;
        st[t.u]=1;
        st[like[t.u]]=1;
        q.push({like[like[t.u]],--in[like[like[t.u]]]});//将他喜欢的人的喜欢的人 入度-1 并放到优先队列中去,此时队列中会有重复的人,但因为按入度排序,并标记了是否被访问过,所以没有影响
    }
    return n-ans;
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&like[i]);//i喜欢like[i]
        in[like[i]]++;
    }
    cout<<solve()<<endl;
    return 0;
}

5. smart robot(dfs)

题意: 给一个n*n的矩阵(1<=矩阵中的数字<=9),可以选定矩阵中任意一个点作为起点,然后可以向上左下右四个方向走任意步,每一个路径我们都会得到一串数字,我们把它转化成十进制数字,求这个矩阵不能得到的最小数字

输入:
4
1 2 3 4
3 6 7 8
0 1 5 4 
9 1 1 1
输出:
17
#include<iostream>
using namespace std;

int n;
int arr[60][60];
int st[10000];
int dx[4]={0,-1,0,1};
int dy[4]={-1,0,1,0};

void dfs(int x,int y,int cnt,int sum)
{
    if(cnt==5) return;
    //cout<<sum<<endl;
    st[sum]=1;
    for(int i=0;i<4;i++)
    {
        int x1=x+dx[i];
        int y1=y+dy[i];
        if(x1>=1&&x1<=n&&y1>=1&&y1<=n)
        {
            dfs(x1,y1,cnt+1,sum*10+arr[x1][y1]);
        }
    }
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&arr[i][j]);
        }
    }

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            dfs(i,j,1,arr[i][j]);
        }
    }

    for(int i=1;i<10000;i++)
    {
        if(!st[i])
        {
            cout<<i<<endl;
            return 0;
        }
    }

    return 0;
}

6. Adventurer’s Guild(二维费用背包)

题意:
给出怪物的数量n,人物血量H,人物的攻击S;
接下来n行,每一行为每只怪物的血量h,攻击s,价值w;
每消灭一个怪物,消耗h的血量和s的攻击,S如果为负数,需要用H去弥补S,如果H为负数则结束;
输出可以获得的最大价值;

输入:
2 66 22
1 23 2
66 8 90
输出:
2

输入:
4 16 22
1 23 11
5 8 14
2 36 99
15 22 27
输出:
27
#include<iostream>
using namespace std;

int n,H,S;
long long dp[310][310];//消耗血量为i,攻击为j的最大价值

int main()
{
    cin >> n >> H >> S;
    int ans = 0;
    for(int i = 1; i <= n; i ++)
    {
        int h,s,w;
        cin >> h >> s >> w;
        for(int j = H; j > h; j --)
        {
            for(int k = S; k >= 0; k --)
            {
                if(j + k > h + s && j > h)
                {
                    if(s > k)//攻击不够,血量补
                    {
                        int x = h + s - k;
                        dp[j][k] = max(dp[j][k],dp[j - x][0] + w);
                    }
                    else//攻击够
                    {
                        dp[j][k] = max(dp[j][k],dp[j - h][k - s] + w);
                    }
                }
            }
        }
    }
    cout << dp[H][S] << endl;
    return 0;
}

7. Matrix Problem(构造)

题意:
给定一个n×m的01矩阵C,要求输出两个相同大小的01矩阵A,B,使得A,B重合为1的地方C为1,其余地方的C为0

输入:
5 5
00000
00100
01010
01100
00000
输出:
11110
10100
11110
11100
11110
00001
01111
01011
01111
00001
#include<iostream>
using namespace std;

int n,m;
char a[510][510];

int main()
{
    cin>>n>>m;
    getchar();
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    for(int i=0;i<n;i++)
    {
        cout<<"1";
        if(i%2==0)//偶行全为1
        {
            for(int j=1;j<m-1;j++) cout<<"1";
        }
        else
        {
            for(int j=1;j<m-1;j++) cout<<a[i][j];
        }
        cout<<"0"<<endl;
    }
    for(int i=0;i<n;i++)
    {
        cout<<"0";
        if(i%2==0)
        {
            for(int j=1;j<m-1;j++) cout<<a[i][j];
        }
        else//奇行全为1
        {
            for(int j=1;j<m-1;j++) cout<<"1";
        }
        cout<<"1"<<endl;
    }
    return 0;
}

8. Longest Continuous 1

题意:
t组数据,每次输入一个n,表示取01串的前n个数,求这前n个数中最长连续1的长度
在这里插入图片描述

在这里插入图片描述

#include<iostream>
using namespace std;

int t,k;

int main()
{
    cin>>t;
    while(t--)
    {
        cin>>k;
        if(k==1)
        {
            cout<<"0"<<endl;
        }
        else
        {
            int sum=1,ans=1;
            for(int i=1; i<=60; i++)
            {
                sum+=(1<<(i-1))*i;
                if(k>=sum+1) ans=i+1;//注意这里的细节,sum+1,sum是加完某一个相同长度的二进制数位后的值,而+1是因为下一个二进制数最高位必然为1
                else break;
            }
            cout<<ans<<endl;
        }
    }
    return 0;
}

9. String Game(dp,类似最长公共子序列)

题意:
给定一个字符串和它的子序列,问这个子序列按顺序可以匹配到原字符串中多少个不同得位置

输入:
eeettt
输出:
9

输入:
eeettt
输出:
0
#include <bits/stdc++.h>
using namespace std;

const int M = 1e9 + 7;
char a[5005], b[1005];
int dp[1005][5005];   //第一位存已经匹配到的数位(子序列),第二位存当前进行到的数位(原序列)
int n, m, i, j;
int main()
{
    scanf("%s%s", a + 1, b + 1);
    memset(dp, 0, sizeof(dp));
    n = strlen(a + 1);
    m = strlen(b + 1);
    for (i = 1; i <= m; i++)
    {
        for (j = 1; j <= n; j++)
        {
            if (a[j] == b[i])
            {
                if (i == 1)
                    dp[i][j] = (1 + dp[i][j - 1]) % M;
                else
                    dp[i][j] = (dp[i - 1][j - 1] + dp[i][j - 1]) % M;
            }
            else
            {
                dp[i][j] = dp[i][j - 1];
            }
        }
    }
    printf("%d\n", dp[m][n]);
    return 0;
}

10. Kobolds and Catacombs

题意:
给定一个数字序列,要求给它划分区间,然后在区间内排序,使该序列成为一个不减序列,问最多可以划分成多少个区间

输入:
5
1 3 2 7 4
输出:
3

O(nlog n)

#include<iostream>
#include<algorithm>
using namespace std;

int n;
int a[1000010],b[1000010];
long long sum1,sum2;

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        sum1+=a[i];
        sum2+=b[i];
        if(sum1==sum2) cnt++;
    }
    cout<<cnt<<endl;
    return 0;
}

O(n)

#include<iostream>
using namespace std;

int n;
int a[1000010],b[1000010];//b存最小后缀,不包括当前位置
int cnt;
int mina=1e9+5;
int maxa;

int main()
{
    cin>>n;
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    for(int i=n-1;i>=0;i--)
    {
        b[i]=mina;
        mina=min(mina,a[i]);
    }
    for(int i=0;i<n;i++)
    {
        maxa=max(maxa,a[i]);
        if(maxa<=b[i]) cnt++;
    }
    cout<<cnt<<endl;
    return 0;
}

11. Build Roads

#include<iostream>
#include<algorithm>
#define int long long
using namespace std;

long long n,L,R;
int a[200020];
long long seed;
int p[200020];
int m;

long long xorshift64()
{
    long long x=seed;
    x^=x<<13;
    x^=x>>7;
    x^x<<17;
    return seed=x;
}

int gen()
{
    return xorshift64()%(R-L+1)+L;
}

struct Edge
{
    int a,b,w;
    bool operator< (const Edge &W)const
    {
        return w<W.w;
    }
}edges[200020];

int find(int x)
{
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}

long long k()
{
    long long res=0;
    sort(edges,edges+m);
    for(int i=1; i<=n; i++) p[i]=i;
    for(int i=0; i<m; i++)
    {
        int a=edges[i].a;
        int b=edges[i].b;
        int w=edges[i].w;
        a=find(a);
        b=find(b);
        if(a!=b)
        {
            p[a]=b;
            res+=w;
        }
    }
    return res;
}

signed main()
{
    scanf("%lld%lld%lld%lld",&n,&L,&R,&seed);
    for(int i=1; i<=n; i++)
    {
        a[i]=gen();
    }
    if(L==R)
    {
        cout<<1ll*L*(n-1)<<endl;
        return 0;
    }
    if(n>7)
    {
        cout<<n-1<<endl;
        return 0;
    }
    if(n<=7)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=i+1;j<=n;j++)
            {
                edges[m++]={i,j,__gcd(a[i],a[j])};
            }
        }
        cout<<k()<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

勇敢nn

心动不如行动,感谢您的支持!

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

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

打赏作者

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

抵扣说明:

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

余额充值