Educational Codeforces Round 101
Educational Codeforces Round 101
我诈尸辣!
A Regular Bracket Sequence
没看到只有一对括号。。
如果只有一对括号的话,NO的情况只有两个:字符串长度为奇数或者第一个字符为右括号。否则将前一半?
变成左括号,后一半?
变成右括号即可。
如果不是只有一对括号的话,就先计算出来所有?中需要变成左括号的有多少,那么把前面的变成左括号,后边的变成右括号,然后括号匹配即可。
#include<bits/stdc++.h>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
#define rsort(x,y) sort(x,y),reverse(x,y)
#define mp(x,y) make_pair(x,y)
#define pir pair<int,int>
#define vi vector<int>
using namespace std;
typedef long long ll;
const int Maxn=110000;
const int Maxm=110000;
const int inf=0x3f3f3f3f;
#ifdef GRAGH
int first[Maxn],nxt[Maxm],to[Maxm],w[Maxm],c[Maxm];
int tot=1;
inline void add(int u,int v) {
to[tot]=v;
nxt[tot]=first[u];
first[u]=tot++;
}
inline void add2(int u,int v) {
add(u,v);
add(v,u);
}
inline void add(int u,int v,int wi) {
to[tot]=v;
nxt[tot]=first[u];
w[tot]=wi;
first[u]=tot++;
}
inline void add2(int u,int v,int wi) {
add(u,v,wi);
add(v,u,wi);
}
inline void add3(int u,int v,int wi) {
add(u,v,wi);
add(v,u,0);
}
inline void add(int u,int v,int wi,int cost) {
to[tot]=v;
nxt[tot]=first[u];
w[tot]=wi;
c[tot]=cost;
first[u]=tot++;
}
inline void add2(int u,int v,int wi,int cost) {
add(u,v,wi,cost);
add(v,u,0,-cost);
}
#endif
template<typename T>
bool read(T &a) {
T f=1; a=0;
char ch=getchar();
while(!isdigit(ch)) {
if(ch=='-') f=-1;
if(ch==EOF) return false;
ch=getchar();
}
while(isdigit(ch)) {
a=a*10+ch-'0';
ch=getchar();
}
a=a*f;
return true;
}
template<typename T1,typename T2>
bool read(T1 &a,T2 &b) {
return read(a)&&read(b);
}
template<typename T1,typename T2,typename T3>
bool read(T1 &a,T2 &b,T3 &c) {
return read(a,b)&&read(c);
}
int n;
char s[Maxn];
int main() {
read(n);
while(n--) {
scanf("%s",s);
int temp=0,k=0;
int len=strlen(s);
for(int i=0;i<len;i++)
if(s[i]=='(') temp--;
else if(s[i]==')') temp++;
else k++;
int tt=0;
bool flag=1;
int sxz=(k-temp)/2;
for(int i=0;i<len;i++) {
if(s[i]=='(') tt++;
else if(s[i]==')') {
if(tt==0) {
puts("NO");
flag=0;
break;
}
tt--;
}
else {
if(sxz)
tt++,sxz--;
else {
if(tt==0) {
puts("NO");
flag=0;
break;
}
tt--;
}
}
}
if(flag) {
if(tt==0)
puts("YES");
else puts("NO");
}
}
return 0;
}
B Red and Blue
求两个串合成的一个串的最大前缀和,那么分别求出两个串的最大前缀和,加起来就行了。
#include<bits/stdc++.h>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
#define rsort(x,y) sort(x,y),reverse(x,y)
#define mp(x,y) make_pair(x,y)
#define pir pair<int,int>
#define vi vector<int>
using namespace std;
typedef long long ll;
const int Maxn=110000;
const int Maxm=110000;
const int inf=0x3f3f3f3f;
#ifdef GRAGH
int first[Maxn],nxt[Maxm],to[Maxm],w[Maxm],c[Maxm];
int tot=1;
inline void add(int u,int v) {
to[tot]=v;
nxt[tot]=first[u];
first[u]=tot++;
}
inline void add2(int u,int v) {
add(u,v);
add(v,u);
}
inline void add(int u,int v,int wi) {
to[tot]=v;
nxt[tot]=first[u];
w[tot]=wi;
first[u]=tot++;
}
inline void add2(int u,int v,int wi) {
add(u,v,wi);
add(v,u,wi);
}
inline void add3(int u,int v,int wi) {
add(u,v,wi);
add(v,u,0);
}
inline void add(int u,int v,int wi,int cost) {
to[tot]=v;
nxt[tot]=first[u];
w[tot]=wi;
c[tot]=cost;
first[u]=tot++;
}
inline void add2(int u,int v,int wi,int cost) {
add(u,v,wi,cost);
add(v,u,0,-cost);
}
#endif
template<typename T>
bool read(T &a) {
T f=1; a=0;
char ch=getchar();
while(!isdigit(ch)) {
if(ch=='-') f=-1;
if(ch==EOF) return false;
ch=getchar();
}
while(isdigit(ch)) {
a=a*10+ch-'0';
ch=getchar();
}
a=a*f;
return true;
}
template<typename T1,typename T2>
bool read(T1 &a,T2 &b) {
return read(a)&&read(b);
}
template<typename T1,typename T2,typename T3>
bool read(T1 &a,T2 &b,T3 &c) {
return read(a,b)&&read(c);
}
int n,m,t;
int a[Maxn],b[Maxn];
int main() {
read(t);
while(t--) {
read(n);
for(int i=1;i<=n;i++) read(a[i]);
read(m);
for(int i=1;i<=m;i++) read(b[i]);
for(int i=1;i<=n;i++) a[i]+=a[i-1];
for(int i=1;i<=m;i++) b[i]+=b[i-1];
int maxn=0,maxm=0;
for(int i=1;i<=n;i++) qmax(maxn,a[i]);
for(int i=1;i<=m;i++) qmax(maxm,b[i]);
printf("%d\n",maxn+maxm);
}
return 0;
}
C Building a Fence
这道题最难的地方在于读懂题意。
在一排高度不一的地面上竖直放若干\(1\times k\)个砖块,相邻砖块之间必须要有公共边,每块地面可以变高,但最多只能变高k-1,两端的地面不能变高。
采用贪心。
首先我们注意到,一个地方如果要变高,只是为了能和相邻的更高的地方的砖块有公共边。而这种变化并不会对比这个地方高的地方造成影响,所以我们将所有的地块从高到低进行考虑。由于我们是按照从高到低进行枚举的,所以我们要让每个地块升高的尽可能小,这个贪心显然是对的。(同样的,你也可以从小到大进行枚举并让每个地块升高尽可能大)
#include<bits/stdc++.h>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
#define rsort(x,y) sort(x,y),reverse(x,y)
#define mp(x,y) make_pair(x,y)
#define pir pair<int,int>
#define vi vector<int>
using namespace std;
typedef long long ll;
const int Maxn=210000;
const int Maxm=110000;
const int inf=0x3f3f3f3f;
#ifdef GRAGH
int first[Maxn],nxt[Maxm],to[Maxm],w[Maxm],c[Maxm];
int tot=1;
inline void add(int u,int v) {
to[tot]=v;
nxt[tot]=first[u];
first[u]=tot++;
}
inline void add2(int u,int v) {
add(u,v);
add(v,u);
}
inline void add(int u,int v,int wi) {
to[tot]=v;
nxt[tot]=first[u];
w[tot]=wi;
first[u]=tot++;
}
inline void add2(int u,int v,int wi) {
add(u,v,wi);
add(v,u,wi);
}
inline void add3(int u,int v,int wi) {
add(u,v,wi);
add(v,u,0);
}
inline void add(int u,int v,int wi,int cost) {
to[tot]=v;
nxt[tot]=first[u];
w[tot]=wi;
c[tot]=cost;
first[u]=tot++;
}
inline void add2(int u,int v,int wi,int cost) {
add(u,v,wi,cost);
add(v,u,0,-cost);
}
#endif
template<typename T>
bool read(T &a) {
T f=1; a=0;
char ch=getchar();
while(!isdigit(ch)) {
if(ch=='-') f=-1;
if(ch==EOF) return false;
ch=getchar();
}
while(isdigit(ch)) {
a=a*10+ch-'0';
ch=getchar();
}
a=a*f;
return true;
}
template<typename T1,typename T2>
bool read(T1 &a,T2 &b) {
return read(a)&&read(b);
}
template<typename T1,typename T2,typename T3>
bool read(T1 &a,T2 &b,T3 &c) {
return read(a,b)&&read(c);
}
int n,m,t;
int a[Maxn];
bool b[Maxn];
pir p[Maxn];
int main() {
read(t);
while(t--) {
read(n,m);
priority_queue<pir> q;
int flag=true;
for(int i=1;i<=n;i++) read(a[i]),q[i]=mp(a[i],i);
rsort(q+1,q+n+1);
for(int i=1;i<=n;i++) {
pir now=q[i];
if(now.second==1) {
if(a[2]-a[1]>=m) flag=false;
}
else if(now.second==n) {
if(a[n-1]-a[n]>=m) flag=false;
}
else {
int x=min(m-1,max(0,max(a[now.second-1]-a[now.second]-m+1,a[now.second+1]-a[now.second]-m+1)));
a[now.second]+=x;
}
}
for(int i=2;i<n;i++)
if(abs(a[i]-a[i-1])>=m||abs(a[i]-a[i+1])>=m) {
flag=false;
}
puts(flag?"YES":"NO");
}
return 0;
}
D Ceil Divisions
构造题。
对于n个数\(1\dots n\),我们可以令任意\(i\not=n,let\ i=\left\lceil \frac{i}{i+1}\right\rceil\),这样可以把除n外的任何数字变成1,但是n就没法变小。
所以我们保留了几个数字:\(2,2^2,2^{2^2},\dots,n=2,4,16,256,\dots,n\)。我们设剩下来的数字为\(a_1,a_2,\dots,a_r\),那么\(\forall i<n,a_i^2\ge a_{i+1}\),那么可以通过两次操作将\(a_{i+1}\)。在数据范围内,总操作次数不超过n+5。
#include<bits/stdc++.h>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
#define rsort(x,y) sort(x,y),reverse(x,y)
#define mp(x,y) make_pair(x,y)
#define pir pair<int,int>
#define vi vector<int>
using namespace std;
typedef long long ll;
const int Maxn=210000;
const int Maxm=110000;
const int inf=0x3f3f3f3f;
#ifdef GRAGH
int first[Maxn],nxt[Maxm],to[Maxm],w[Maxm],c[Maxm];
int tot=1;
inline void add(int u,int v) {
to[tot]=v;
nxt[tot]=first[u];
first[u]=tot++;
}
inline void add2(int u,int v) {
add(u,v);
add(v,u);
}
inline void add(int u,int v,int wi) {
to[tot]=v;
nxt[tot]=first[u];
w[tot]=wi;
first[u]=tot++;
}
inline void add2(int u,int v,int wi) {
add(u,v,wi);
add(v,u,wi);
}
inline void add3(int u,int v,int wi) {
add(u,v,wi);
add(v,u,0);
}
inline void add(int u,int v,int wi,int cost) {
to[tot]=v;
nxt[tot]=first[u];
w[tot]=wi;
c[tot]=cost;
first[u]=tot++;
}
inline void add2(int u,int v,int wi,int cost) {
add(u,v,wi,cost);
add(v,u,0,-cost);
}
#endif
template<typename T>
bool read(T &a) {
T f=1; a=0;
char ch=getchar();
while(!isdigit(ch)) {
if(ch=='-') f=-1;
if(ch==EOF) return false;
ch=getchar();
}
while(isdigit(ch)) {
a=a*10+ch-'0';
ch=getchar();
}
a=a*f;
return true;
}
template<typename T1,typename T2>
bool read(T1 &a,T2 &b) {
return read(a)&&read(b);
}
template<typename T1,typename T2,typename T3>
bool read(T1 &a,T2 &b,T3 &c) {
return read(a,b)&&read(c);
}
int n,m,t;
int a[Maxn];
int b[Maxn];
pir p[Maxn];
int main() {
read(t);
ll x=2;
while(x<Maxn) a[x]=1,x=x*x;
while(t--) {
read(n);
int tot=0;
m=0;
for(int i=2;i<n;i++) if(a[i]==0) {
p[++tot]=mp(i,i+1);
}
else {
b[++m]=i;
}
b[++m]=n;
for(int i=m;i>1;i--) p[++tot]=mp(b[i],b[i-1]),p[++tot]=mp(b[i],b[i-1]);
printf("%d\n",tot);
for(int i=1;i<=tot;i++) {
printf("%d %d\n",p[i].first,p[i].second);
}
}
return 0;
}
E A Bit Similar
CF也烤串?我是不是已经跟不上时代了?
考虑问题的本质。观察到串中只有01两种字符,那么如果一个长度为k的串与原串的任意一个长度为k的子串是a bit similar的,那么将原串按位取反后该串就与这个子串相等。
所以问题就变成了找到原串按位取反后其中没有出现的字典序最小的长度为k的串。
考虑SAM。对按位取反后的串建SAM。取反后的串的每一个子串都能找到一个对应的SAM中的节点。如果一个节点的下面的一个节点为空,那么说明对应于这个节点的子串加上某个字符后就无法在取反的串中找到了。所以我们可以做一遍dfs,算出每个节点距离最近的没有某个儿子的节点的距离。
接下来的就是贪心。通过上面算出来的距离,我们可以知道一个儿子能不能走。那么我们优先向0儿子走,这样求出来的答案字典序最小,且能保证我们一定能求出来一个答案。
最后答案如果不够长的话就全部填0就好了。
#include<bits/stdc++.h>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
#define rsort(x,y) sort(x,y),reverse(x,y)
#define mp(x,y) make_pair(x,y)
#define pir pair<int,int>
#define vi vector<int>
using namespace std;
typedef long long ll;
const int Maxn=2100000;
const int Maxm=110000;
const int inf=0x3f3f3f3f;
#ifdef GRAGH
int first[Maxn],nxt[Maxm],to[Maxm],w[Maxm],c[Maxm];
int tot=1;
inline void add(int u,int v) {
to[tot]=v;
nxt[tot]=first[u];
first[u]=tot++;
}
inline void add2(int u,int v) {
add(u,v);
add(v,u);
}
inline void add(int u,int v,int wi) {
to[tot]=v;
nxt[tot]=first[u];
w[tot]=wi;
first[u]=tot++;
}
inline void add2(int u,int v,int wi) {
add(u,v,wi);
add(v,u,wi);
}
inline void add3(int u,int v,int wi) {
add(u,v,wi);
add(v,u,0);
}
inline void add(int u,int v,int wi,int cost) {
to[tot]=v;
nxt[tot]=first[u];
w[tot]=wi;
c[tot]=cost;
first[u]=tot++;
}
inline void add2(int u,int v,int wi,int cost) {
add(u,v,wi,cost);
add(v,u,0,-cost);
}
#endif
template<typename T>
bool read(T &a) {
T f=1; a=0;
char ch=getchar();
while(!isdigit(ch)) {
if(ch=='-') f=-1;
if(ch==EOF) return false;
ch=getchar();
}
while(isdigit(ch)) {
a=a*10+ch-'0';
ch=getchar();
}
a=a*f;
return true;
}
template<typename T1,typename T2>
bool read(T1 &a,T2 &b) {
return read(a)&&read(b);
}
template<typename T1,typename T2,typename T3>
bool read(T1 &a,T2 &b,T3 &c) {
return read(a,b)&&read(c);
}
int ch[Maxn][2],len[Maxn],link[Maxn],cnt=1;
int last,root;
int newnode(int lenn=0) {
int now=++cnt;
len[now]=lenn;
return now;
}
void cp(int x,int y) {
ch[x][0]=ch[y][0],ch[x][1]=ch[y][1];
link[x]=link[y];
}
void extend(int x) {
int cur=newnode(len[last]+1);
int p=last; last=cur;
while(p&&!ch[p][x]) {
ch[p][x]=cur;
p=link[p];
}
if(p) {
int q=ch[p][x];
if(len[q]==len[p]+1) link[cur]=q;
else {
int clone=newnode(len[p]+1);
cp(clone,q),link[q]=link[cur]=clone;
while(p&&ch[p][x]==q) {
ch[p][x]=clone;
p=link[p];
}
}
}
else link[cur]=root;
}
int t,n,k,l;
char s[Maxn];
char ans[Maxn];
int vis[Maxn];
void dfs(int root) {
if(vis[root]!=inf) return;
dfs(ch[root][0]);
dfs(ch[root][1]);
vis[root]=min(vis[ch[root][0]],vis[ch[root][1]])+1;
}
int main() {
read(t);
memset(vis,0x3f,sizeof(vis));
while(t--) {
read(n,k);
scanf("%s",s);
last=newnode(0);
root=last;
for(int i=0;i<n;i++) extend(1-(s[i]-='0'));
l=0;
for(int i=root;i<=cnt;i++) if(ch[i][0]==0||ch[i][1]==0) vis[i]=0;
for(int i=root;i<=cnt;i++) if(vis[i]==inf) dfs(i);
if(vis[root]<k) {
puts("YES");
int now=root;
int temp=k;
while(1) {
temp--;
if(!ch[now][0]) {
putchar('0');
while(temp--)
putchar('0');
break;
}
if(vis[ch[now][0]]<temp) {
putchar('0');
now=ch[now][0];
continue;
}
if(!ch[now][1]) {
putchar('1');
while(temp--)
putchar('0');
break;
}
putchar('1');
now=ch[now][1];
}
puts("");
}
else puts("NO");
}
return 0;
}
F Power Sockets
此题仍然贪心。。。今天这是贪心专场吗?
首先,如果要挂一个串,那么最好挂最中间的位置以减小深度。
显然,越长的串越早挂更好。因为不同长度的串挂上去的花费是一样的,而更长的串挂上去的收益更多,且随着时间的增长,收益还会变得更多。
但我们并不是能挂就挂。如果在某个点挂了一个串,就会导致这一层少了一个点,且下一层不会多一点。收益要到两层之后才能体现出来。所以挂一个串只会对下一层造成影响,两层之后就会产生收益。
所以当我们发现下一层就能凑齐k个白点时,我们就不去挂串了,毕竟挂了也只会让两层之后多点。
而如果发现加上下一层还凑不齐,那我们就能挂就挂。
#include<bits/stdc++.h>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
#define rsort(x,y) sort(x,y),reverse(x,y)
#define mp(x,y) make_pair(x,y)
#define pir pair<int,int>
#define vi vector<int>
using namespace std;
typedef long long ll;
const int Maxn=2100000;
const int Maxm=110000;
const int inf=0x3f3f3f3f;
#ifdef GRAGH
int first[Maxn],nxt[Maxm],to[Maxm],w[Maxm],c[Maxm];
int tot=1;
inline void add(int u,int v) {
to[tot]=v;
nxt[tot]=first[u];
first[u]=tot++;
}
inline void add2(int u,int v) {
add(u,v);
add(v,u);
}
inline void add(int u,int v,int wi) {
to[tot]=v;
nxt[tot]=first[u];
w[tot]=wi;
first[u]=tot++;
}
inline void add2(int u,int v,int wi) {
add(u,v,wi);
add(v,u,wi);
}
inline void add3(int u,int v,int wi) {
add(u,v,wi);
add(v,u,0);
}
inline void add(int u,int v,int wi,int cost) {
to[tot]=v;
nxt[tot]=first[u];
w[tot]=wi;
c[tot]=cost;
first[u]=tot++;
}
inline void add2(int u,int v,int wi,int cost) {
add(u,v,wi,cost);
add(v,u,0,-cost);
}
#endif
template<typename T>
bool read(T &a) {
T f=1; a=0;
char ch=getchar();
while(!isdigit(ch)) {
if(ch=='-') f=-1;
if(ch==EOF) return false;
ch=getchar();
}
while(isdigit(ch)) {
a=a*10+ch-'0';
ch=getchar();
}
a=a*f;
return true;
}
template<typename T1,typename T2>
bool read(T1 &a,T2 &b) {
return read(a)&&read(b);
}
template<typename T1,typename T2,typename T3>
bool read(T1 &a,T2 &b,T3 &c) {
return read(a,b)&&read(c);
}
int t,n,k,l;
int a[Maxn],b[Maxn];
int main() {
read(n,k);
for(int i=1;i<=n;i++) read(a[i]);
rsort(a+1,a+n+1);
ll s=1;
for(int i=1;i<=n;i++) s+=a[i]-2;
if(s<k) return 0*puts("-1");
b[0]++;
b[1]--;
int temp=0;
int j=1;
for(int i=0;;i++) {
temp+=b[i];
b[i+1]+=b[i];
if(temp+b[i+1]>=k) {
printf("%d\n",i+1);
return 0;
}
while(temp&&j<=n) {
temp--;
b[i+2]++;
b[i+2]++;
int x=a[j]-1;
b[i+2+x/2]--;
b[i+2+x-x/2]--;
j++;
}
}
return 0;
}