Educational Codeforces Round 101 (Rated for Div. 2)
由于题目都是英文的,贴在这里也没有什么意义,就不贴了。
还是这里只贴出前四道简单的题的思路。
A. Regular Bracket Sequence
T组( T∈[1,1000])数据,每组数据:给出只包含一个“( ”和一个“ )”,其它位置都是“ ?”的长度大于等于2的字符串S。求能否填充“ ?” 为“ )”或“( ”使得S串成为一个好的括号序列。
思路是判断S第一位不是“)”,最后一位不是“(”,且S长度为偶数。时间复杂度O(n)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
string f(string s){
return s.size()%2==0&&s[0]!=')'&&s[s.size()-1]!='('?"YES":"NO";
}
int main(){
int t;
cin>>t;
while(t--){
string s;
cin>>s;
cout<<f(s)<<endl;
}
return 0;
}
B. Red and Blue
T组( T∈[1,1000] )数据,每组给你一个长度为n的序列和一个长度为m的序列(n,m∈[1,100]),这两个序列要组合成为一个长度为n+m的新序列,但每个数字在原序列的相对顺序不能改变,有 2 n + m 2^{n+m} 2n+m种组合方式,求这些组合方式中,序列前缀和的最大值。
思路是把这两个序列的前缀和最大值相加,因为我们求的就是这两个序列的前缀和的最大值,也就是分别求出这两个序列的前缀和最大值。时间复杂度O(n)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int n,m;
cin>>n;
int u=0,tot=0;
for(int i=0;i<n;i++){
int x;
cin>>x;
tot+=x;
u=max(u,tot);
}
cin>>m;
int v=0;
tot=0;
for(int i=0;i<m;i++){
int x;
cin>>x;
tot+=x;
v=max(v,tot);
}
cout<<u+v<<endl;
}
return 0;
}
C. Building a Fence
T组( T∈[1,10000] )数据,每组给你一个长度为n (n∈[2,2e5])的数组和一个数字k(k∈[2,1e8]),代表平地的高度和积木的固定高度,问能否按照题目规则摆放完所有的n个积木。
思路是递推,知道第i个积木的放置范围,就能推出第i+1个积木的放置范围。时间复杂度O(n)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=200010;
typedef long long LL;
LL a[N];
int n;
LL k;
bool ck(){
LL l=a[1]+1,r=a[1]+1;
for(int i=2;i<n;i++){
LL ll=a[i]+1,rr=a[i]+k;
if(ll>r+k-1||rr+k-1<l){
return 0;
}
l=max(ll,l-k+1);
r=min(rr,r+k-1);
// cout<<l<<' '<<r<<endl;
}
if(l>a[n]+k||r+k-1<a[n]+1){
return 0;
}
return 1;
}
int main(){
int t;
cin>>t;
while(t--){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
if(ck()){
cout<<"YES"<<endl;
}else{
cout<<"NO"<<endl;
}
}
return 0;
}
D. Ceil Divisions
T组( T∈[1,1000] )数据,每组给你一个数n( n∈[3,2e5]),问将序列 [1,2,3,…,n]通过任意位置间除法上取整运算得到序列[1,2,1,1,1,1…,1]需要多少步。
题目要求是步数不能大于n+5。
最简单的方法就是3/4,4/5,5/6,… ,最后用n一直除以2,但这需要n+logn步,n大于1000就不符合要求了。
于是想到利用倍数差,先把[1,2,3,…,n]转化为[1,2,1,4,1,1,1,8,…,16,…,32,…,1024,…,n],再从后往前把这几个没有变成1的数用两步变成1。。。。这也需要n+logn步。。。。。
于是把倍数差变大,经计算变成8可以卡过n+5,因为n等于2e5的时候logn小于18,则 l o g 8 n log_8n log8n<=6,最多多出7步(8的倍数那6步加上n多出的1步),然而变[1,2,3,…,n]为[1,2,1,1,1,1,1,8,…,64,…,512,…,n]需要n-2-7步,(n-2-7)+7*2=n+5,正巧卡过。(相同代码可以合并,奈何智商没有那么高,当时有心无力,现在也不想改了。)
还有一种方法,是标准解法。只看n,用(i-1)/i,遇到根号n就用n除以根号n。最后3到n-1都一次变到1,n用不到5次变到1。
这题考数论,n变到2的最快方法是开根号。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=200010;
typedef long long LL;
void show(int n){
if(n==8){
cout<<5<<' '<<6<<endl;
cout<<6<<' '<<7<<endl;
cout<<7<<' '<<8<<endl;
cout<<8<<' '<<4<<endl;
cout<<8<<' '<<2<<endl;
cout<<3<<' '<<4<<endl;
cout<<4<<' '<<2<<endl;
cout<<4<<' '<<2<<endl;
return;
}
for(int i=n/8+1;i<=n-1;i++){
cout<<i<<' '<<i+1<<endl;
}
cout<<n<<' '<<n/8<<endl;
cout<<n<<' '<<8<<endl;
show(n/8);
}
void solve(int n){
if(n<8){
cout<<(n-2)*2<<endl;
for(int i=n;i>2;i--){
cout<<i<<' '<<i-1<<endl;
cout<<i<<' '<<2<<endl;
}
return;
}else if(n==8){
cout<<8<<endl;
show(8);
return;
}
int x=64,cnt=0;
while(x<n){
cnt++;
x*=8;
}
cout<<n+cnt+1<<endl;
if(n>64*8*8*8){
for(int i=64*8*8*8+1;i<=n-1;i++){
cout<<i<<' '<<i+1<<endl;
}
cout<<n<<' '<<64*8*8*8<<endl;
cout<<n<<' '<<8<<endl;
show(64*8*8*8);
}else if(n>64*8*8){
for(int i=64*8*8+1;i<=n-1;i++){
cout<<i<<' '<<i+1<<endl;
}
cout<<n<<' '<<64*8*8<<endl;
cout<<n<<' '<<8<<endl;
show(64*8*8);
}else if(n>64*8){
for(int i=64*8+1;i<=n-1;i++){
cout<<i<<' '<<i+1<<endl;
}
cout<<n<<' '<<64*8<<endl;
cout<<n<<' '<<8<<endl;
show(64*8);
}else if(n>64){
for(int i=64+1;i<=n-1;i++){
cout<<i<<' '<<i+1<<endl;
}
cout<<n<<' '<<64<<endl;
cout<<n<<' '<<8<<endl;
show(64);
}else if(n>8){
for(int i=8+1;i<=n-1;i++){
cout<<i<<' '<<i+1<<endl;
}
cout<<n<<' '<<8<<endl;
cout<<n<<' '<<8<<endl;
show(8);
}
}
int main(){
int t;
cin>>t;
while(t--){
int n;
cin>>n;
solve(n);
}
return 0;
}