思维题目专练

大概是一些CCPC和ICPC的铜牌左右的题目和部分比较好的题,还有一些常见的trick

题意和思路都简要写了,更多的时间要放在刷题上,还剩2-3周的时间,破釜沉舟,只愿不留遗憾。

I. Interesting Permutation(铜,递推,构造)
题意:要构造排列p,给定h数组,hi是排列p的前缀i的最大值和最小值的差,给定h数组请你求出p的构造方式有多少种。

思路:递推,当h更新说明最大值或者最小值更新,方案数乘二同时更新最大值最小值又多产生了max-min个中间的数,不变说明从中间的数中选了一个放进去。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 1e5+100;
int a[N];
map<int,int>mp;
const int mod = 1e9+7;
signed main()
{
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t;
    for(cin>>t;t;t--)
    {
        int n;
        cin>>n;
        bool falg=true;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            if(a[i]>n-1)
                falg=false;
        }
        if(a[1]!=0||a[n]!=n-1)
            falg=false;
        int sum=0;
        int ans=1;
        for(int i=2;i<=n;i++)
        {
            if(a[i]>a[i-1])
            {
                ans=ans*2%mod;
                sum+=a[i]-a[i-1]-1;
            }
            if(a[i]==a[i-1])
            {
                ans=ans*sum%mod;
                sum--;
            }
            if(a[i]<a[i-1])
                falg=false;
        }
        if(falg)
        cout<<ans<<endl;
        else
        cout<<0<<endl;
    }
   return 0;
}

I. Power and Zero(铜,二进制,贪心)
题意:给定一组数a,你每次可以安排一个数组b,b的元素是a数组中的并且可以重复选择同一个元素,让b的每个位置都减去2i-1,请问最少多少次操作可以让数组变为0。

思路:我们从低位二进制位开始,每次尽可能向高位进发,也就是取最长的一段连续二进制位让每一位都减少1,如果遍历到的这个二进制位没有了,那么向高位去借。

#include<bits/stdc++.h>
#define int long long
#define endl '\n'

using namespace std;
const int N=1e5+5;
int n,a[N],sum;
int bits[50];
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int T;cin>>T;
    while(T--)
    {
        int n,sum=0;
        memset(bits,0,sizeof(bits));
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            int temp=a[i];
            int cnt=0;
            while(temp)
            {
                if(temp&1)
                {
                    bits[cnt]++;
                    sum++;
                }
                cnt++;
                temp>>=1;
            }
        }
        int ans=0;
        while(sum>0)
        {
            ans++;
            for(int i=0;i<=30;i++)
            {
                if(bits[i]>0)
                {
                    bits[i]--;
                    sum--;
                }
                else
                {
                    for(int j=i;j<=30;j++)
                    {
                        if(bits[j])
                        {
                            for(int k=j;k>i;k--)
                            {
                                bits[k]--;
                                 bits[k-1]+=2;
                                sum++;
                            }
                            bits[i]--;
                            sum--;
                            break;
                        }
                    }
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

E - Power and Modulo (签,数学)
题意:给定一个数组,请你找出一个唯一确定的数m,使得数组满足等式 a[i]=2i-1%m

思路:找到第一个不等于2i-1的a[i],不难看出m是介于(2i-2,2i-1]之间的所以不存在倍数关系,可以唯一确定一个数m=2i-1-a[i],然后对于整个数组验证一遍即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 1e6+100;
int a[N];
int two[N];
signed main()
{
    //cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t;
    for(cin>>t;t;t--)
    {
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        int pos=0;
        bool falg=true;
        for(int i=1;i<=n;i++)
        {
            if(a[i]!=(1ll<<(i-1)))
            {
                if(a[i]>(1ll<<(i-1)))
                {
                    falg=false;
                }
                pos=i;
                break;
            }
        }
        if(!pos)
            falg=false;
        int mod;
        if(falg)
        {
            mod=(1ll<<(pos-1))-a[pos];
            two[0]=1ll%mod;
            for(int i=1;i<=n;i++)
            {
                two[i]=two[i-1]*2ll%mod;
            }
            for(int i=1;i<=n;i++)
            {
                //cout<<two[i-1]<<" "<<a[i]<<endl;
                if(two[i-1]!=a[i])
                {
                    falg=false;
                }
            }
        }
        if(falg)
            cout<<mod;
        else
            cout<<-1;
        if(t!=1)
            cout<<endl;
    }
   return 0;
}

E. Prof. Pang and Poker(银,博弈,分类讨论)

在这里插入图片描述

思路:分类讨论,具体看代码就可以了。

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); ++i)
using namespace std;
const int N = 1e5 + 55;
int n, m;
int a[N], b[N];

int getcard() {
    static char s[5];
    cin >> s;
    if(s[0] == 'T') return 10;
    else if(s[0] == 'J') return 11;
    else if(s[0] == 'Q') return 12;
    else if(s[0] == 'K') return 13;
    else if(s[0] == 'A') return 14;
    else return s[0] - '0';
}

signed main(){
    //freopen("E.in", "r", stdin);
    ios::sync_with_stdio(false); cin.tie(nullptr);
    int Task; cin >> Task;
    while(Task--) {
        cin >> n >> m;
        int A0_max = -1, A1_max = -1, B0_max = -1, B1_max = -1;
        int A0 = 0, B0 = 0, A1 = 0, B1 = 0;
        int X;
        rep(i, 1, n) a[i] = getcard();
        rep(i, 1, m) b[i] = getcard();
        X = getcard();
        rep(i, 1, n) {
            int y = a[i];
            if(y < X) ++A0, A0_max = max(A0_max, y);
            else ++A1, A1_max = max(A1_max, y);
        }
        rep(i, 1, m) {
            int y = b[i];
            if(y < X) ++B0, B0_max = max(B0_max, y);
            else ++B1, B1_max = max(B1_max, y);
        }
        int ok = 0;
        if(A0 == 1 && A1 == 0) ok = 0;
        else if(A0 == 0) ok = 0;
        else if(A0 >= 1) {
            if(B0 >= 2) ok = 1;
            else if(B0 == 0) ok = 0;
            else if(B0 == 1) {
                if(A0_max < B0_max) ok = 0;
                else {
                    if(B1 == 0) ok = 1;
                    else if(B1 > 0 && A1 == 0) ok = 0;
                    else if(A1_max > B1_max) {
                        if(A0 == 1 || A0 + A1 <= 3) ok = 0;
                        else ok = 1;
                    }
                    else ok = 0;
                }
            }
        }
        ok ? puts("Pang") : puts("Shou");
    }
    return 0;
}

I. Dragon Bloodline(银,二分答案)
题意:现在有龙要下蛋,下一个蛋需要n种材料每个材料要a[i]个,有k个等级的工具龙,每个等级的工具龙有b[i]条,等级为i的工具龙可以收集2i-1个材料,现在想要下蛋数量最大化,请你求出最多能下多少蛋。

思路:二分答案,,也没啥好说的,注意控制上界别超过longlong,注意控制下界到0

贪心策略就是先用最强的工具龙,然后在不超过每个材料要求的情况下去收集材料,多余的工具龙优先去收集目前剩余需求量最大的材料。

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define endl '\n'
const int N = 1e6+100;
int a[N],b[N];
int ta[N],tb[N];
int n,k;
bool cmp(const int a,const int b)
{
    return a>b;
}
bool check(int x)
{
    for(int i=1;i<=n;i++)
        ta[i]=a[i]*x;
    for(int i=1;i<=k;i++)
        tb[i]=b[i];
    for(int i=k;i>=1;i--)
    {
        for(int j=1;j<=n;j++)
        {
            if(!tb[i])
                break;
            int num=min(tb[i],ta[j]/(1<<(i-1ll)));
            ta[j]-=num*(1ll<<(i-1ll));
            tb[i]-=num;
        }
        if(tb[i])
        {
            tb[i]=min(tb[i],n);
            sort(ta+1,ta+n+1,cmp);
            //nth_element(ta+1,ta+tb[i],ta+n+1,greater<int>());
            for(int j=1;j<=tb[i];j++)
            {
                ta[j]=0;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(ta[i])
            return false;
    }
    return true;
}
signed main()
{
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t;
    for(cin>>t;t;t--)
    {
        int suma=0,sumb=0;
        cin>>n>>k;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            suma+=a[i];
        }
        for(int i=1;i<=k;i++)
        {
            cin>>b[i];
            sumb+=(b[i]*(1<<(i-1)));
        }
        int l=0,r=sumb/suma,ans=0;
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(check(mid))
                l=mid+1,ans=mid;
            else
                r=mid-1;
        }
        cout<<ans<<endl;
    }
    return 0;
}


E - Crystal Switches(铜,BFS最短路,分层图)
题意:一张图上n个点m条边,每条边权值为1可以走,为0不能走,有k个节点上有开关,启用开关之后全部的边都节点权值-1
请你求出节点1到n的最短步数。

思路:BFS最短路,所有含有开关的点视为有一个派生节点,记录一下当前开关使用次数,每次走边的时候判断通透性即可。

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define lowbit(x) ((x)&(-x))
#define int long long
//#define ll long long
#define pause system("pause")
const int mod=1e9+7;
const int inf=1e18;
const int N = 1e6+100;
const double eps=1e-10;

int qpow(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int sgn(double x)
{
    if(fabs(x)<eps) return 0;
    else if(x<0) return -1;
    else return 1;
}
int getinv(int a){return qpow(a,mod-2LL);}
int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
int head[N],cnt;
struct Edge
{
    int next,to,w;
}e[N];
void addedge(int from,int to,int w)
{
    e[++cnt].next=head[from];
    e[cnt].to=to;
    e[cnt].w=w;
    head[from]=cnt;
}
int n,m,k,s[N],vis[N][2];
struct node
{
    int id,dis,fg;
};
void bfs()
{
    queue<node>q;
    q.push(node{1,0,0);
    if(s[1])
    q.push(node{1,0,1});
    int ans=-1;
    while(!q.empty())
    {
        int u=q.front().id,dis=q.front().dis,fg=q.front().fg;q.pop();
        if(u==n){ans=dis;break;}
        if(vis[u][fg]) continue;
        vis[u][fg]=1;
        for(int i=head[u];i;i=e[i].next)
        {
            int j=e[i].to;
            if((fg^e[i].w)==0) continue;
            q.push(node{j,dis+1,fg});
            if(s[j]) q.push(node{j,dis+1,fg^1});
        }
    }
    cout<<ans<<endl;
}
signed main()
{
    //ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++) vis[i][0]=vis[i][1]=s[i]=0;
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        cin>>u>>v>>w;
        addedge(u,v,w);
        addedge(v,u,w);
    }
    for(int i=1;i<=k;i++)
    {
        int x;cin>>x;s[x]=1;
    }
    bfs();
    pause;
    return 0;
}

C - Ladder Takahashi(签,节点离散化+DFS)
题意:n个梯子,每个梯子链接的楼层是a和b,现在你从第1层开始,请问最高能爬到多高。

思路:由于楼层最大取值1e9,所以一开始考虑离散化并查集,写挂了()

然后想着直接建图用DFS全部跑一遍就好了,然后就过了。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 2e6+100;
int num[N];
map<int,int>mp;
struct node
{
    int nex,to;
}edge[N<<1];
int head[N],tot;
void add(int from,int to)
{
    edge[++tot].to=to;
    edge[tot].nex=head[from];
    head[from]=tot;
}
int maxx=1;
int vis[N];
void DFS(int now,int fa)
{
    if(vis[now])
        return ;
    maxx=max(num[now],maxx);
    vis[now]=1;
    for(int i=head[now];i;i=edge[i].nex)
    {
        int to=edge[i].to;
        if(to==fa||vis[to])
            continue;
        DFS(to,now);
    }
}
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int n;
    cin>>n;
    int cnt=1;
    mp[1]=1;
    num[1]=1;
    for(int i=1;i<=n;i++)
    {
        int a,b;
        cin>>a>>b;
        if(!mp[a])
        {
            mp[a]=++cnt;
            num[cnt]=a;
        }
        if(!mp[b])
        {
            mp[b]=++cnt;
            num[cnt]=b;
        }
        //cout<<mp[a]<<" "<<mp[b]<<endl;
        add(mp[a],mp[b]);
        add(mp[b],mp[a]);
    }
    DFS(1,0);
    cout<<maxx;
    return 0;
}

D - Takahashi’s Solitaire(签,循环数组)
题意:有n个小于m大于等于0的数,第一次打出这些数中任意一个,设截止当前时刻最后一次打出的数是x,随后每次可以打出值为x或者(x+1)%m的数。

请问什么策略下,打出数之后,剩下的数的和最小

思路:显然,构成了一个环,我们的任务就是把和最大的一段弧给从整个环里减去。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 2e6+100;
int num[N];
map<int,int>mp;
struct node
{
    int nex,to;
}edge[N<<1];
int head[N],tot;
void add(int from,int to)
{
    edge[++tot].to=to;
    edge[tot].nex=head[from];
    head[from]=tot;
}
int maxx=1;
int vis[N];
void DFS(int now,int fa)
{
    if(vis[now])
        return ;
    maxx=max(num[now],maxx);
    vis[now]=1;
    for(int i=head[now];i;i=edge[i].nex)
    {
        int to=edge[i].to;
        if(to==fa||vis[to])
            continue;
        DFS(to,now);
    }
}
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int n;
    cin>>n;
    int cnt=1;
    mp[1]=1;
    num[1]=1;
    for(int i=1;i<=n;i++)
    {
        int a,b;
        cin>>a>>b;
        if(!mp[a])
        {
            mp[a]=++cnt;
            num[cnt]=a;
        }
        if(!mp[b])
        {
            mp[b]=++cnt;
            num[cnt]=b;
        }
        //cout<<mp[a]<<" "<<mp[b]<<endl;
        add(mp[a],mp[b]);
        add(mp[b],mp[a]);
    }
    DFS(1,0);
    cout<<maxx;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值