【题目记录】——ICPC青岛2018


题目集地址 ICPC青岛2018
提交题目地址 ZOJ
4058-4070
C D E F J M K

C Flippy Sequence 组合数学 分类

题目地址C Flippy Sequence
题目大意:给出两个01字符串s,t,长度不超过 1 0 6 10^6 106,规定一种操作,每次操作可以将区间[l,r]内的所有元素取反,现在想要将s经过两次操作之后变成t,询问有多少种方案可以满足(将两次操作[l_1,r_1],[l_2,r_2]构成一个四元组 (l_1,r_1,l_2,r_2),两种方案不同只要四元组有一个元素不同即可)
思路:
两区间异或一下,分段考虑,有四种情况
1.如果全为0则任选两相同区间,答案为 C n + 1 2 = n ( n + 1 ) 2 C^2_{n+1}=\frac{n(n+1)}{2} Cn+12=2n(n+1)
2.只有一段连续的1,则两区间有一个公共边界,另外两个边界分别为连续1的左右边界,答案为 2 C n − 1 1 = 2 ( n − 1 ) 2C^1_{n−1}=2(n−1) 2Cn11=2(n1)
3.有两段则两区间平分四个边界,答案为C^2_4=6
4.三段以上无解。
AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
char a[N],b[N];
int n;
ll ans;
int main() {
    int T;
    for(scanf("%d",&T); T--;) {
        scanf("%d%s%s",&n,a,b);
        for(int i=0; i<n; ++i)a[i]=a[i]==b[i]?'0':'1';
        int t=0;
        for(int i=0; i<n; ++i)if(a[i]=='1'&&a[i+1]!='1')++t;
        if(t==0)ans=(ll)n*(n+1)/2;
        else if(t==1)ans=(ll)(n-1)*2;
        else if(t==2)ans=6;
        else ans=0;
        printf("%lld\n",ans);
    }
    return 0;
}

D Magic Multiplication

题目地址D Magic Multiplication
题目大意:对于给定的两个数A,B,假设 A = a 1 a 2 … a n , B = b 1 b 2 … b m A=a_1a_2\dots a_n,B=b_1b_2\dots b_m A=a1a2an,B=b1b2bm分别为A , B A,BA,B对应位上的数(十进制),定义一种运算 A ⨂ B = ∑ i = 1 n ∑ j = 1 m a i b j = a 1 b 1 + a 1 + ⋯ + a 1 b m + a 2 b 1 + … A \bigotimes B=\sum_{i=1}^{n}\sum_{j=1}^{m}a_ib_j=a_1b_1+a_1+\dots+a_1b_m+a_2b_1+\dots AB=i=1nj=1maibj=a1b1+a1++a1bm+a2b1+现在给出运算结果,求出A,B,如果答案有多个,以最小的A为基准,如果仍有多个,以最小的B为基准。
思路:有一个规律:只要A的第一个数确定了,那么剩下的数都可以推出来,而且推的过程中C中不可能出现一位和两位数都可行的情况,因此没有回溯的过程,直接枚举A的第一个数然后判断是否可行就行了

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=2e5+5;
int T,n,m,a[maxn],b[maxn];
char c[maxn];
bool GetB() {
    int len=strlen(c),pos=0;
    for(int i=0; i<m; i++) {
        if(pos==len)return 0;//字符串不够用,不合法
        int x=c[pos++]-'0';//获得对应数字
        if(pos<len&&x&&x<a[0])x=x*10+c[pos++]-'0';
        //如果字符串够用,这一位不为0,数字小于a[0],就需要多拿一位
        if(x%a[0]||x/a[0]>9)return 0;
        //如果不能整除或者商为两位数
        b[i]=x/a[0];//构造b[i]
    }
    for(int i=1; i<n; i++)
        for(int j=0; j<m; j++) {
            if(pos==len)return 0;
            int x=c[pos++]-'0';//获得对应数字
            if(pos<len&&x&&x<b[j])x=x*10+c[pos++]-'0';
            //如果字符串够用,这一位不为0,数字小于a[0],就需要多拿一位
            if(x&&(b[j]==0||(j&&a[i]==0)))return 0;
            //如果有结果但是b的对应位为0或者a的对应位为0(j代表已经计算过,根据0~j-1推出a[i]==0)
            if(x==0) {
                if(j&&a[i]&&b[j])return 0;
                //结果有0并且a[i]已经算过,b的对应为有值
                if(!j)a[i]=0;//如果第一次算,结果为0,则a[i]为0
            } else {
                if (x%b[j]||j&&x/b[j]!=a[i]||x/b[j]>9) return 0;
                //如果不能整除或者商为两位数
                a[i]=x/b[j];
            }
        }
    return pos==len;
}
bool Judge(int x) {
    for(int i=1; i<=9; i++)
        if(x%i==0) {
            a[0]=i;
            if(GetB())return 1;
        }
    return 0;
}
bool solve() {
    int x=c[0]-'0';
    if(Judge(x))return 1;
    x=x*10+c[1]-'0';
    if(Judge(x))return 1;
    return 0;
}
int main() {
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d%s",&n,&m,&c);
        if(solve()) {
            for(int i=0; i<n; i++)printf("%d",a[i]);
            putchar(' ');
            for(int i=0; i<m; i++)printf("%d",b[i]);
            putchar('\n');
        } else printf("Impossible\n");
    }
    return 0;
}

E Plants vs. Zombies

题目地址E Plants vs. Zombies
题目大意:n个植物,坐标为1 ~ n,给出每个植物浇一次水之后的增长量,现从i=0开始移动,可以到达数轴的任意地方,每次移动一步,最多只能移动m步,每次移动之后,如果当前下标有植物,则必须浇水,现在求m次之后所有植物的最小高度的最大值是多少
思路:二分查找答案,然后从左往右走,如果遇到一个点处的值没有满足条件,则在它和它后面的数之间来回走动,判断所需步数是否小于等于m即可。注意即使当前的数已经满足条件了,也要往后走一步(最后一格除外)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
const ll inf=0x3f3f3f3f3f3f3f3f;
int n;
ll m,a[N],b[N];
bool ok(ll x) {
    ll cnt=m;
    for(int i=0; i<n; ++i)a[i]=0;
    for(int i=0; i<n; ++i) {
        if(a[i]<x) {
            ll t=(x-a[i]-1)/b[i]+1;
            a[i]+=b[i]*t,a[i+1]+=b[i+1]*(t-1);
            cnt-=2*t-1;
        } else {
            if(i==n-1)return 1;
            cnt--;
        }
        if(cnt<0)return 0;
    }
    return cnt>=0;
}
ll bi(ll l,ll r) {
    while(l<r) {
        ll mid=(l+r)>>1;
        ok(mid+1)?l=mid+1:r=mid;
    }
    return l;
}
int main() {
    int T;
    for(scanf("%d",&T); T--;) {
        scanf("%d%lld",&n,&m);
        for(ll i=0; i<n; ++i)scanf("%lld",&b[i]);
        printf("%lld\n",bi(0,inf));
    }
    return 0;
}

F Tournament

题目地址F Tournament
题目大意:n个骑士比赛,k个回合,安排骑士们每一回合的对战对手,满足以下条件

  • 每一个骑士在每一回合必须有对手
  • 每两个骑士在k个回合里只能打一次
  • i不等于j,在第i个回合中,如果ab,cd为两组,那么在第j个回合中必须是ac,bd为一组
    思路:就是循环赛日程表问题,可以打一个1024*1024的日程表,然后判断前k个回合中是否有不满足条件的回合,如果有就是Impossible。
    n为奇数的话必定不满足条件
    AC代码:
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=1e3+55;
int a[N][N];
bool flag;
void table()
{
    int n=1024;
    a[1][1]=1;
    for(int m = 1;m <= n/2;m*=2)
    {
        for(int i = 1;i <= m;i++)
        {
            for(int j = 1;j <= m;j++)
            {
                a[i][j+m] = a[i][j]+m;
                a[i+m][j+m] = a[i][j];
                a[i+m][j] = a[i][j+m];
            }
        }
    }
}
void solve()
{
    int n,k;
    scanf("%d%d",&n,&k);
    flag=true;
    for(int i = 2;i <= k+1;i++)
        {
            for(int j = 1;j <= n;j++)
            {
                if(a[i][j]>n||a[i][j]==0)
                    flag=false;
            }
        }
    if(n&1||!flag)
    {
        printf("Impossible\n");
    }
    else
    {
        for(int i = 2;i <= k+1;i++)
        {
            for(int j = 1;j <= n;j++)
            {
                printf("%d",a[j][i]);
                if(j<n)
                {
                    printf(" ");
                }
                else
                    printf("\n");
            }
        }
    }
}

int main()
{
	freopen("in.txt","r",stdin);
//	int t = 1;
	int t;
	scanf("%d",&t);
	table();
	while(t--)
	{
		solve();
	}
    return 0;
}

J Books

题目地址J Books
题目大意:小a去书店买书,有n本书,从1-n,小a一次查看书的价格,只要身上的钱足够买这本书,他就买下。给出n本书的价格和小a买下了m本书。问小a最多带了多少钱
思路:首先读入数据,统计一下价格为0的书的个数cnt0,如果大于m就是impossible
等于m,就是所有非0价格的书的最小值减1
大于m,就买下最前面的非0价格的书(m-cnt0)本,加上后面所有非零价格的书的价格的最小值-1即可。
AC代码:

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=1e6+6;
ll a[N];
void solve()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int cnt0=0;
    ll minx=INF;
    for(int i = 1;i <= n;i++)
    {
        scanf("%lld",a+i);
        if(a[i]==0)
        {
            cnt0++;
        }
        else
        {
            minx=min(minx,a[i]);
        }
    }
    if(n==m)
    {
        printf("Richman\n");
        return;
    }
    ll res = 0;
    if(cnt0>m)
    {
        printf("Impossible\n");
    }
    else if(cnt0 == m)
    {
        printf("%lld\n",minx-1);
    }
    else
    {
        minx=INF;
        m-=cnt0;
        int i = 1;
        for(;i <= n&&m;i++)
        {
            if(a[i])
            {
                res+=a[i];
                m--;
            }
        }
        for(;i <= n;i++)
        {
            if(a[i])
            {
                minx=min(minx,a[i]);
            }
        }
        res+=(minx-1);
        printf("%lld\n",res);
    }
}

int main()
{
//	freopen("in.txt","r",stdin);
//	int t = 1;
	int t;
	scanf("%d",&t);
	while(t--)
	{
		solve();
	}
    return 0;
}

M Function and Function

题目地址M Function and Function
题目大意:定义了两个函数f(x)和g(x),定义内容看原题即可
思路:签到题,找规律即可,
输入的x为0时要单独考虑。

#include <bitsdc++.h>

#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
const int mod = 1e9+7;
int a[10]= {1,0,0,0,1,0,1,0,2,1};
int cal(int x) {
    int res=0;
    if(x==0)
        return 1;
    while(x>0) {
        int yu=x%10;
        x/=10;
        res+=a[yu];
    }
    return res;
}
void solve() {
    int x,k;
    scanf("%d%d",&x,&k);
    int temp1=x;
    for(int i=1; i<=k; i++) {
        temp1=cal(temp1);
        if(temp1==0) {
            if((k-i)%2==0)
                temp1=0;
            else
                temp1=1;
            break;
        } else if(temp1==1) {
            if((k-i)%2==0)
                temp1=1;
            else
                temp1=0;
            break;
        }
    }
    printf("%d\n",temp1);
}

int main() {
    int t = 1;
    scanf("%d",&t);
    while(t--) {
        solve();
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值