今晚CF鸽了,陪跑完AB就睡大觉去,傻逼学校那服务器我真服了,电费分几次交交不上的,直接没空调过了一晚。
Codeforces 1202A 题目链接:点击这里传送
题意:
给定两个二进制下的数字A和B,要求另A+B
×
\times
×
2
k
2^k
2k的结果倒置后最小。求这时的k。
思路:
B
×
\times
×
2
k
2^k
2k就类似于十进制的$\times$10,就是往后面加0。找到B串最后一次出现1的位置,通过加0的方式让B串这位上的1尽快和A串的1匹配上。(未匹配时为1,匹配后1+1=0,成功减小了字典序)
输出的就是这两位之间的差值
#include<bits/stdc++.h>
using namespace std;
string s1, s2;
int t;
int ans;
int pos;
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> t;
while (t--)
{
ans = 0;
cin >> s1 >> s2;
int cnt = 0;
for (int i = s2.length() - 1; i >= 0; i--)
{
if (s2[i] == '1') break;
cnt++;
}
for (int i = s1.length()-1-cnt; i >= 0; i--)
{
if (s1[i] == '1') break;
ans++;
}
cout << ans << endl;
}
return 0;
}
Codeforces 1234C 题目链接:点击这里传送
题意:
有上述的6种水管。1和2可以互相转化,3和4和5和6可以互相转化。要求从(1,1)走到(2,n),水管怎么摆看上面。按照给出的水管能不能到达终点?
思路:
如果都是1或2,状态不变。
如果都是3或4或5或6,状态改变(上下层变换)
如果当前在上层且上层为弯管道下层为直管道,管道泄漏。
如果当前在下层且上层为直管道下层为弯管道,管道泄漏。
#include<bits/stdc++.h>
using namespace std;
int t, n;
string s1, s2;
int f;//0表示第一层,1表示第二层
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> t;
while (t--)
{
cin >> n;
f = 0;
cin >> s1 >> s2;
for (int i = 0; i < n; i++)
{
if (s1[i] >= '3' && s2[i] <= '2'&&f==0 || s1[i] <= '2' && s2[i] >= '3'&&f==1)
{
f = 0;
break;
}
if (s1[i] >= '3' && s2[i] >= '3')
{
f = 1-f;
}
}
if (f) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}
Codeforces 1234B1/B2 题目链接:点击这里传送
题意:
一个人的手机发来了n条消息,但屏幕只能装下k条消息。如果这条消息的发送人以前发送的消息正出现在屏幕上,则忽略这条他现在发来的消息。否则,如果屏幕满了,把屏幕上最早的消息删了,再接着把这条消息放在屏幕的最后边。
思路:
用双向队列维护屏幕上的信息,用map判断重复性。
#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
int n,k;
int a[MAXN];
deque <int> q;
map <int,int> cnt;
int num;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(cnt[a[i]]==0)
{
cnt[a[i]]++;
if(q.size()==k)
{
cnt[q.front()]--;
q.pop_front();
num--;
}
q.push_back(a[i]);
num++;
}
}
cout<<num<<endl;
for(int i=num-1;i>=0;i--) cout<<q[i]<<" ";
return 0;
}
Codeforces 841B 题目链接:点击这里传送
题意:
一共有n个元素,两名选手每次可以再满足各自规定的前提下去任意多的元素。Alice只能去元素和为奇数,Bob只能取元素和为偶数。问谁最后胜利。
思路:
元素和为奇数。必然由奇数+偶数得到,Alice必胜。
元素和为偶数。可以奇数+奇数,可以偶数+偶数。换言之,只要有一个元素是奇数,就可以转变成Alice必胜。
好像写这破玩意有点用,之前写了一篇偶数+奇数=奇数思想做出来的题,印象很深刻,这题一分钟就做出来了。
#include<bits/stdc++.h>
using namespace std;
int f;
int n;
int temp;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>temp;
if(temp%2==1)
{
f=1;
break;
}
}
if(f) cout<<"First";
else cout<<"Second";
return 0;
}
Codeforces 1200B 题目链接:点击这里传送
题意:
有n个长度为
h
i
h_i
hi的方块,你要从起点1走到终点n,一共有三种操作,有没有可能走到终点。
- 往下凿方块并放入袋子(MC左键)
- 往下垫方块(MC搭高台)
- 如果 h i h_i hi和 h i + 1 h_{i+1} hi+1的差的绝对值小于k,直接跨过去。
思路:
每次都尽可能的往下凿方块,使袋子里的方块数量尽可能多,但得特判凿到地面的情况,这时就不能继续挖下去了。
#include<bits/stdc++.h>
using namespace std;
int t,n,m,k,f;
#define MAXN 105
int a[MAXN];
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>t;
while(t--)
{
cin>>n>>m>>k;//几列方块,袋子里有几个方块,最多跨多少
for(int i=1;i<=n;i++) cin>>a[i];
f=1;
if(n==1)
{
cout<<"YES"<<endl;
continue;
}
for(int i=1;i<n;i++)
{
if(a[i]>=a[i+1])//尽可能移走最多方块
{
m+=min(a[i],(a[i]-a[i+1]+k));//这里卡了一次,不能贪心成负数
}
else if(a[i+1]-a[i]<=k)//尽可能的移走最多方块
{
m+=min(a[i],(k-(a[i+1]-a[i])));//一样
}
else if(a[i+1]-a[i]>k)//自己垫方块
{
if(m+a[i]+k<a[i+1])
{
f=0;
break;
}
else
{
m-=(a[i+1]-a[i]-k);
}
}
}
if(f) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
Codeforces 1547E 题目链接:点击这里传送
题意:
n个空地上有m个空调,空调旁边的温度以公差为1向两列递增。求n个空地各自的温度(取最小值)
思路:
只考虑左边的,n跑过去;只考虑右边的,n跑过去。温度就是min(
l
i
l_i
li,
r
i
r_i
ri)。
#include<bits/stdc++.h>
using namespace std;
#define MAXN 300005
#define ll long long
ll l[MAXN];
ll r[MAXN];
ll a[MAXN];
ll pos[MAXN];
int t,n,m;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>t;
while(t--)
{
cin>>n>>m;
memset(a,0x3f3f3f3f,sizeof(a));
for(int i=1;i<=m;i++) cin>>pos[i];
for(int i=1;i<=m;i++) cin>>a[pos[i]];
ll p=999999999999;
for(int i=1;i<=n;i++)
{
p=min(a[i],p+1);
l[i]=p;
}
p=999999999999;
for(int i=n;i>0;i--)
{
p=min(a[i],p+1);
r[i]=p;
}
for(int i=1;i<=n;i++)
{
cout<<min(l[i],r[i])<<" ";
}
cout<<endl;
}
return 0;
}
Codeforces 1547F 题目链接:点击这里传送
题意:
一共n个数。每次操作后会产生一个新的数组,他的各个元素为:gcd(
a
1
a_1
a1,
a
2
a_2
a2),gcd(
a
2
a_2
a2,
a
3
a_3
a3)…gcd(
a
n
a_n
an,
a
1
a_1
a1)
求需要几次这样的操作能使数组中的所有元素值相同。
思路:
最终的元素确定不下来,很难办,假设确定的话只能为1。操作就是 a i a_i ai/gcd( a 1 a_1 a1, a 2 a_2 a2,…, a n a_n an)
最后要求首尾gcd,可以拆环为链,把前n-1个数再复制到后面使得新数组的元素个数为2 × \times ×n-1
如果两个数互质,那么只需一次操作。
如果两个数有一个公因数2,一次操作后
a
i
a_i
ai为2,
a
i
+
1
a_{i+1}
ai+1未知,需要原始的
a
i
+
2
a_{i+2}
ai+2来确定。
对一次操作后的
a
i
+
1
a_{i+1}
ai+1进行讨论
- a i + 1 a_{i+1} ai+1不为2,此时原始值没有因子2,这样需要2次操作就可使 a i a_i ai变为1
- a i + 1 a_{i+1} ai+1为2,此时原始值有因子2,经过第二轮操作后 a i a_i ai为2,要使 a i a_i ai为1要看接下来的 a i + 3 a_{i+3} ai+3, a i + 4 a_{i+4} ai+4,…,甚至 a i + n a_{i+n} ai+n
通过以上例子可以观察发现,若
a
i
a_i
ai,
a
i
+
1
a_{i+1}
ai+1,
a
i
+
2
a_{i+2}
ai+2,…,
a
i
+
k
a_{i+k}
ai+k都有公因子2,
a
i
+
k
+
1
a_{i+k+1}
ai+k+1没有公因子2,那么需要k+1次操作才能消为1。
所以,策略就是找到第一次满足gcd(
a
i
a_i
ai,
a
i
+
1
a_{i+1}
ai+1,
a
i
+
2
a_{i+2}
ai+2,…,
a
i
+
k
a_{i+k}
ai+k)=1的序列,答案就是k。
区间gcd用线段树维护,找范围用二分(左端点 a i a_i ai遍历过去枚举,右端点用二分答案)。答案就是各个范围的最大值。
#include<bits/stdc++.h>
using namespace std;
#define MAXN 400005
#define ll long long
ll a[MAXN];
int t,n;
ll gcd(ll a,ll b)
{
if(a<b) swap(a,b);
ll temp;
while(b)
{
temp=a%b;
a=b;
b=temp;
}
return a;
}
struct segtreenode
{
int l,r;
ll val;//区间内所有数的gcd
}tree[MAXN*4];
void pushup(int rt){tree[rt].val=gcd(tree[rt<<1].val,tree[rt<<1|1].val);}
void build(int rt,int l,int r)
{
tree[rt].l=l;tree[rt].r=r;
if(l==r)
{
tree[rt].val=a[l];
return;
}
int mid=(l+r)>>1;
if(mid>=l) build(rt<<1,l,mid);
if(mid<r) build(rt<<1|1,mid+1,r);
pushup(rt);
}
ll query(int rt,int l,int r)
{
if(tree[rt].l>r||tree[rt].r<l) return 0;
if(tree[rt].l>=l&&tree[rt].r<=r) return tree[rt].val;
return gcd(query(rt<<1,l,r),query(rt<<1|1,l,r));
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<n;i++) a[i+n]=a[i];
ll pub=a[1];
for(int i=1;i<n;i++) pub=gcd(pub,a[i+1]);
if(pub!=1)
{
for(int i=1;i<=2*n-1;i++) a[i]=a[i]/pub;
}
//for(int i=1;i<=n;i++) cout<<a[i]<<" ";
// cout<<endl;
build(1,1,2*n-1);
int ans=0;
for(int i=1;i<=n;i++)
{
if(a[i]==1) continue;
int l=i+1;int r=i+n;
while(l<r)
{
int mid=(l+r)/2;
if(query(1,i,mid)==1)
{
r=mid;
}
else
{
l=mid+1;
}
}
ans=max(ans,r-i);
}
cout<<ans<<endl;
}
return 0;
}
Codeforces 1538A 题目链接:点击这里传送
题意:
有一个拳击手,他可以选择数组中最左或最右的元素将其击碎。问最少需要几次这样的过程能把数组中的最大值和最小值都击碎了。
思路:
分类讨论。击碎同一边分两种,左右向中间击碎也可以分两种。
#include<bits/stdc++.h>
using namespace std;
int t,n;
#define MAXN 105
int a[MAXN];
int maxi,mini;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]==1) mini=i;
if(a[i]==n) maxi=i;
}
int ans1=max(mini,maxi);
int ans2=n-min(mini,maxi)+1;
int ans3=mini+n-maxi+1;
int ans4=maxi+n-mini+1;
int ans=min(ans1,ans2);
ans=min(ans,ans3);
ans=min(ans,ans4);
cout<<ans<<endl;
}
return 0;
}