文章目录
题目集地址 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)
2Cn−11=2(n−1)
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=a1a2…an,B=b1b2…bm分别为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
A⨂B=∑i=1n∑j=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;
}