Codeforce Round #457 & Round #458

Round #457 B. Jamie and Binary Sequence
题意:给定n和k,要求将n表示为k个2的幂之和,输出最高幂最小且字典序最大的结果。

题解:首先将n一位一位地拿出来,如果位数大于k肯定是无法合并了,无解。如果位数小于k就需要根据2^m=2^(m-1)+2^(m-1)将其中的某些项分解。每次将最大的幂分解成两倍数目的较小的幂,这样保证了最大的幂最小。如果此时的数目超过了k,就取消这位的分解,改为从最低位开始一个一个地向下分解,这样保证了字典序最大。

#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<map>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 200050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

ll n,k;
int a[maxn],*cnt=a+100000;

int main()
{
    cin>>n>>k;
    for(int i=0;i<60;i++)
    {
        cnt[i]+=n>>i&1;
        k-=cnt[i];
    }
    if(k<0)
    {
        printf("No\n");
        return 0;
    }
    for(int i=59;i>=-60;i--)
    {
        if(k>=cnt[i])
        {
            k-=cnt[i];
            cnt[i-1]+=(2*cnt[i]);
            cnt[i]=0;
        }
        else
        {
            int minn=-60;
            while(!cnt[minn])
                minn++;
            while(k--)
            {
                cnt[minn]--;
                cnt[--minn]+=2;
            }
            break;
        }
    }
    printf("Yes\n");
    for(int i=59;i>=-100000;i--)
    {
        while(cnt[i]--)
            printf("%d ",i);
    }
    printf("\n");
    return 0;
}

Round #457 D. Jamie and To-do List
题意:有若干个事件,每一个事件都有一个优先级,要求维护一个用于记录事件的清单,支持以下操作:
a.将优先级为x的事件t放入清单中;
b.将事件t移出清单;
c.查询清单中优先级高于事件t的事件数目;
d.撤销之前的k个操作(不含此操作)。

题解:支持撤销操作需要使用可持久化线段树。建立一棵树保存权值的数量,另一棵树保存事件的信息。实际上这两棵树可以合并起来,只需要保存两个root即可。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<string>
#include<queue>
#include<map>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 100050
#define maxm 10000050
#define INF 1000000000
#define eps 1e-8
using namespace std;
typedef long long ll;
const ll mod=1000000007;

char op[10];
string s;
map<string,int>m;
int n,cnt,tmp,tot,x,no;
int rot[maxn],newrt[maxn];
struct node
{
    int ls,rs;
    int sum;
    int rt;
}tree[maxm];

int getid(string s)
{
    if(m.count(s))return m[s];
    m[s]=++cnt;
    return cnt;
}

void update(int root,int &newrt,int l,int r,int aim,int v)
{
    newrt=++tot;
    tree[newrt].sum=tree[root].sum+v;
    tree[newrt].ls=tree[root].ls,tree[newrt].rs=tree[root].rs;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(aim<=mid)update(tree[root].ls,tree[newrt].ls,l,mid,aim,v);
    else update(tree[root].rs,tree[newrt].rs,mid+1,r,aim,v);
}

int query(int root,int l,int r,int aiml,int aimr)
{
    if(!root)return 0;
    if(l>=aiml&&r<=aimr)return tree[root].sum;
    int mid=(l+r)>>1,ans=0;
    if(aiml<=mid)ans+=query(tree[root].ls,l,mid,aiml,aimr);
    if(aimr>mid)ans+=query(tree[root].rs,mid+1,r,aiml,aimr);
    return ans;
}

int main()
{
    scanf("%d",&n);
    cnt=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",op);
        rot[i]=rot[i-1];
        newrt[i]=newrt[i-1];
        if(op[0]=='s')
        {
            cin>>s;
            scanf("%d",&x);
            no=getid(s);
            int tmp=query(newrt[i],1,n,no,no);
            if(tmp)update(rot[i],rot[i],1,INF,tmp,-1);
            update(newrt[i],newrt[i],1,n,no,x-tmp);
            update(rot[i],rot[i],1,INF,x,1);
        }
        else if(op[0]=='r')
        {
            cin>>s;
            no=getid(s);
            int tmp=query(newrt[i],1,n,no,no);
            if(tmp)update(rot[i],rot[i],1,INF,tmp,-1);
            update(newrt[i],newrt[i],1,n,no,-tmp);
        }
        else if(op[0]=='q')
        {
            cin>>s;
            no=getid(s);
            int tmp=query(newrt[i],1,n,no,no);
            if(tmp==0)printf("-1\n");
            else if(tmp==0)printf("0\n");
            else printf("%d\n",query(rot[i],1,INF,1,tmp-1));
            fflush(stdout);
        }
        else if(op[0]=='u')
        {
            scanf("%d",&x);
            rot[i]=rot[i-x-1];
            newrt[i]=newrt[i-x-1];
        }
    }
    return 0;
}

Round #458 C. Travelling Salesman and Special Numbers
题意:定义一种操作的返回结果为原数字二进制中1的数目。现给定k和二进制数字n,问所有不大于n的正整数中恰好经过k次操作得到1的数字的数目。

题解:给定的n不超过2^1000,也就是说经过一次操作以后的结果必然在1000之内。处理给定范围所有的数字显然办不到,但处理1000以内的结果还是可以的。设dp[i][j]为s的长度为i的子串中,含有j个1的子串个数。经过推导可以得到:
这里写图片描述
而对于所要求的问题,设tmp为数字i中1的数目,cnt[i]为数字i变换到1所需的操作数,显然有cnt[i]=cnt[tmp]+1。先处理1-1000的cnt值,那么1-n中cnt值为k的数字数目即为cnt值为k-1的数目之和,即:
这里写图片描述

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<string>
#include<queue>
#include<map>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 1050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
const ll mod=1000000007;

int len,a[maxn];
ll k,cnt[maxn];
ll dp[maxn][maxn],com[maxn][maxn];
char s[maxn];
ll c(int a,int b){return a<b?0:com[a][b];}

void init()
{
    com[1][0]=com[1][1]=1;
    for(int i=2;i<=1000;i++)
    {
        com[i][0]=1;
        for(int j=1;j<=1000;j++)
            com[i][j]=(com[i-1][j]+com[i-1][j-1])%mod;
    }
}

int getpre(ll x)
{
    int sum=0;
    while(x)
    {
        if(x%2==1)sum++;
        x/=2;
    }
    return sum;
}

int main()
{
    scanf("%s",s);
    len=strlen(s);
    scanf("%d",&k);
    init();
    for(int i=len-1;i>=0;i--)
        a[len-i]=s[i]-'0';
    if(a[1])dp[1][1]=dp[1][0]=1;
    else dp[1][1]=0,dp[1][0]=1;
    for(int i=2;i<=len;i++)
    {
        dp[i][0]=1;
        for(int j=1;j<=len;j++)
        {
            if(a[i])dp[i][j]=(dp[i-1][j-1]+c(i-1,j))%mod;
            else dp[i][j]=dp[i-1][j];
        }
    }
    ll ans=0;
    for(int i=1;i<=1000;i++)
    {
        int tmp=getpre(i);
        if(i==1)cnt[i]=0;
        else cnt[i]=cnt[tmp]+1;
        if(cnt[i]==k-1)
            ans=(ans+dp[len][i])%mod;
    }
    if(k==0)ans=1;
    else if(k==1)ans=dp[len][1]-1;
    printf("%I64d\n",ans);
    return 0;
}

Round #458 D. Bash and a Tough Math Pazzle
题意:给定一个数列,要求维护这个数列支持两种操作:
a.将数列的第x项改为y;
b.判断对于数列的第l项至第r项,是否可以通过改变其中的不超过1个数字使得这段子序列的最大公约数为x。

题解:线段树单点更新可以很容易地实现更改操作。而对于查询判断操作,可得到引理:更改1个以内数字使得子序列最大公约数为x的充要条件是这个子序列中至多有1个元素不能被x整除。线段树维护区间的GCD,每次查询时返回子序列中不能被x整除的元素数目即可。
引理的证明:若数列的GCD为x,那么数列中每个元素必定能被x整除。
i).数列中有1个以上的元素不能被x整除,更改1个以内任何数字都使得更改后的数列至少有1个元素不被x整除,数列GCD必不为x。
ii).数列中有1个元素不被x整除,将此数更改为x即可使得数列GCD为x。
iii).数列所有元素均能被x整除,数列GCD必为x或x的倍数。GCD为x时无需更改,GCD为x的倍数时将其中任意一元素改为x即可保证数列GCD为x。

#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<map>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 500050
#define INF 1000000000
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,m,l,r,x,flag;
int tree[maxn*4],a[maxn];

int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}

void build(int root,int l,int r)
{
    if(l==r)
    {
        tree[root]=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(root*2,l,mid);
    build(root*2+1,mid+1,r);
    tree[root]=gcd(tree[root*2],tree[root*2+1]);
}

void update(int root,int l,int r,int aim,int x)
{
    if(l==r&&l==aim)
    {
        tree[root]=x;
        return;
    }
    if(l>aim||r<aim)
        return;
    int mid=(l+r)>>1;
    update(root*2,l,mid,aim,x);
    update(root*2+1,mid+1,r,aim,x);
    tree[root]=gcd(tree[root*2],tree[root*2+1]);
}

int query(int root,int l,int r,int aiml,int aimr)
{
    if(l>=aiml&&r<=aimr)
    {
        if(tree[root]%x==0)
            return 0;
        if(l==r)
        {
            if(tree[root]%x==0)
                return 0;
            else return 1;
        }
    }
    if(l>aimr||r<aiml)
        return 0;
    int mid=(l+r)>>1;
    int ansx=query(root*2,l,mid,aiml,aimr);
    if(ansx>1)return 2;
    return ansx+query(root*2+1,mid+1,r,aiml,aimr);
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    build(1,1,n);
    scanf("%d",&m);
    for(int i=0;i<m;i++)
    {
        scanf("%d",&flag);
        if(flag==1)
        {
            scanf("%d%d%d",&l,&r,&x);
            if(query(1,1,n,l,r)<=1)
                printf("YES\n");
            else
                printf("NO\n");
        }
        else if(flag==2)
        {
            scanf("%d%d",&l,&x);
            update(1,1,n,l,x);
        }
    }
    return 0;
}
### 回答1: #include <stdio.h> int main(){ //定义一个3*3的数组用来存储棋盘 int board[3][3]={0}; int x,y,turn=1; while (1){ //打印棋盘 printf("当前棋盘:\n"); for (x=0;x<3;x++){ for (y=0;y<3;y++){ printf("%d ",board[x][y]); } printf("\n"); } //根据turn的值来判断谁轮到落子 if (turn==1){ printf("轮到X落子,请输入落子的位置(x y):"); }else { printf("轮到O落子,请输入落子的位置(x y):"); } scanf("%d %d",&x,&y); //将落子位置的值设置为对应的值 board[x][y] = turn; //改变轮到谁落子 turn = -turn; //判断谁赢了 if (board[0][0]==board[1][1] && board[1][1]==board[2][2] && board[2][2]!=0){ printf("游戏结束,获胜者是%c\n",board[0][0]==1?'X':'O'); break; } if (board[2][0]==board[1][1] && board[1][1]==board[0][2] && board[0][2]!=0){ printf("游戏结束,获胜者是%c\n",board[2][0]==1?'X':'O'); break; } for (x=0;x<3;x++){ if (board[x][0]==board[x][1] && board[x][1]==board[x][2] && board[x][2]!=0){ printf("游戏结束,获胜者是%c\n", board[x][0] == 1 ? 'X' : 'O'); break; } if (board[0][x]==board[1][x] && board[1][x]==board[2][x] && board[2][x]!=0){ printf("游戏结束,获胜者是%c\n", board[0][x] == 1 ? 'X' : 'O'); break; } } } return 0; } ### 回答2: 为了回答这个问题,需要提供题目的具体要求和规则。由于提供的信息不够具体,无法为您提供准确的代码。但是,我可以给您一个简单的Tic-tac-toe游戏的示例代码,供您参考: ```c #include <stdio.h> #include <stdbool.h> // 判断游戏是否结束 bool isGameOver(char board[][3]) { // 判断每行是否有3个相同的棋子 for(int i = 0; i < 3; i++) { if(board[i][0] != '.' && board[i][0] == board[i][1] && board[i][0] == board[i][2]) { return true; } } // 判断每列是否有3个相同的棋子 for(int i = 0; i < 3; i++) { if(board[0][i] != '.' && board[0][i] == board[1][i] && board[0][i] == board[2][i]) { return true; } } // 判断对角线是否有3个相同的棋子 if(board[0][0] != '.' && board[0][0] == board[1][1] && board[0][0] == board[2][2]) { return true; } if(board[0][2] != '.' && board[0][2] == board[1][1] && board[0][2] == board[2][0]) { return true; } return false; } // 输出棋盘 void printBoard(char board[][3]) { for(int i = 0; i < 3; i++) { for(int j = 0; j < 3; j++) { printf("%c ", board[i][j]); } printf("\n"); } } int main() { char board[3][3]; // 初始化棋盘 for(int i = 0; i < 3; i++) { for(int j = 0; j < 3; j++) { board[i][j] = '.'; } } int player = 1; // 玩家1先下 int row, col; while(true) { printf("Player %d's turn:\n", player); printf("Row: "); scanf("%d", &row); printf("Column: "); scanf("%d", &col); // 判断输入是否合法 if(row < 0 || row >= 3 || col < 0 || col >= 3 || board[row][col] != '.') { printf("Invalid move. Try again.\n"); continue; } // 下棋 board[row][col] = (player == 1) ? 'X' : 'O'; // 输出棋盘 printBoard(board); // 判断游戏是否结束 if(isGameOver(board)) { printf("Player %d wins!\n", player); break; } // 切换玩家 player = (player == 1) ? 2 : 1; } return 0; } ``` 这段代码实现了一个简单的命令行下的Tic-tac-toe游戏。玩家1使用'X'棋子,玩家2使用'O'棋子。玩家依次输入行和列,下棋后更新棋盘,并判断游戏是否结束。当游戏结束时,会输出获胜者并结束游戏。 ### 回答3: 题目要求实现一个井字棋游戏的判断胜负函数。给定一个3x3的井字棋棋盘,用C语言编写一个函数,判断当前是否存在某个玩家获胜或者平局。 题目要求代码中定义一个3x3的字符数组board来表示棋盘,其中 'X' 表示玩家1在该位置放置了一个棋子, 'O' 表示玩家2在该位置放置了一个棋子, '.' 表示该位置没有棋子。 下面是实现此题的C语言代码: ```c #include <stdio.h> #include <stdbool.h> // 用于使用bool类型 bool checkWin(char board[3][3]) { // 检查每一行是否有获胜的情况 for (int row = 0; row < 3; row++) { if (board[row][0] == board[row][1] && board[row][1] == board[row][2] && board[row][0] != '.') { return true; } } // 检查每一列是否有获胜的情况 for (int col = 0; col < 3; col++) { if (board[0][col] == board[1][col] && board[1][col] == board[2][col] && board[0][col] != '.') { return true; } } // 检查对角线是否有获胜的情况 if ((board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != '.') || (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != '.')) { return true; } return false; // 没有获胜的情况 } int main() { char board[3][3]; // 存储棋盘状态 // 读取棋盘状态 for (int i = 0; i < 3; i++) { scanf("%s", board[i]); } // 调用检查胜负的函数,并输出结果 if (checkWin(board)) { printf("YES\n"); } else { printf("NO\n"); } return 0; } ``` 这个程序中定义了一个函数checkWin,用于检查是否有玩家获胜。遍历棋盘的每一行、每一列和对角线,判断是否有连续相同的字符且不为'.',如果有,则返回true;否则返回false。 在主函数main中,首先定义一个3x3的字符数组board,然后通过循环从标准输入中读取棋盘状态。接着调用checkWin函数进行胜负判断,并根据结果输出"YES"或者"NO"。最后返回0表示程序正常结束。 请注意,该代码只包含了检查胜负的功能,并没有包含其他如用户输入、判断平局等功能。如果需要完整的游戏代码,请告知具体要求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值