【题解】Educational Codeforces Round 72
因为最近不想做简单题,所以只有 C-E
C.The Number Of Good Substrings
题目描述:
题目分析:
我们发现一个长度为 \(len\) 的二进制串能表示的长度最多就是 \(2^{len}-1\),所以我们就能很明显的发现一个性质:我们枚举的区间长度不可能超过 \(\log n\),因为如果超过这个数值很显然整个区间的长度都不够。所以我们就暴力每一个长度小于等于 \(\log n\) 的区间,然后判断长度是否符合条件就好了。
注意:这里的区间长度是指的这个二进制串里第一个 \(1\) 开始的长度,所以可能也允许前面有一些 \(0\),所以也需要考虑这种情况。
代码详解:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int nxt[10000000];
int main(){
int t;
cin>>t;
while(t--){
string s;
cin>>s;
nxt[s.size()] = s.size(); //nxt[i] 即 i 开始下一个 1 的位置
for(int i = s.size()-1; i>=0; i--){ //注意这里是倒序
if(s[i] == '0') nxt[i] = nxt[i+1];
else nxt[i] = i;
}
int ans = 0;
for(int i=0; i<s.size(); i++){ //我们会发现随着 i 的增加前面无论多少个 0 的情况,都会被考虑到
int now = 0;
for(int j=nxt[i]; j <= min(int(s.size()-1),nxt[i]+20); j++){ //log n 大约是 20
now = now * 2 + s[j] - '0'; // j 其实可以理解为右端点
if(now == j - i + 1)
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}
D.Coloring Edges
题目描述:
题目分析:
我们发现环有可能是大环套小环,所以如果考虑在图上找环再进行一系列操作显然行不通。
我们考虑什么还与一个图的环有些关系:树。因为树有一个很奇妙的性质,任加一条返祖边都会形成环。
那么我们就考虑对有向图求一棵生成树,这里是写的 \(\text{DFS}\) 树。对于树边我们就将颜色设为 \(1\),对于返祖边将颜色设为 \(2\),因为返祖边会与树边形成环,对于其他的边我们也都设为 \(1\),因为其他的边不可能与树边形成环,却有可能与返祖边形成环,而这样做颜色也是用的最少的。
代码详解:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5e3+10;
const int MAXM = 5e3+10;
struct edge{
int nxt,to,pos;
edge(){}
edge(int _nxt,int _to,int _pos){
nxt = _nxt,to = _to,pos = _pos;
}
}e[MAXM];
int head[MAXN],ans[MAXM],vis[MAXN],cnt;
void add_edge(int from,int to,int pos){
e[++cnt] =edge(head[from],to,pos);
head[from] = cnt;
}
void dfs(int now){
vis[now] = 1;
for(int i = head[now]; i;i = e[i].nxt){
int to = e[i].to;
if(vis[to] == 0){
ans[e[i].pos] = 1;
dfs(to);
}
else if(vis[to] == 1){
ans[e[i].pos] = 2;
}
else
ans[e[i].pos] = 1;
}
vis[now] = 2;
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1; i<=m; i++){
int from,to;
cin>>from>>to;
add_edge(from,to,i);
}
for(int i=1; i<=n; i++){
if(vis[i] == 0)
dfs(i);
}
bool flag = false;
for(int i=1; i<m; i++){
if(ans[i] != ans[i+1])
flag = true;
}
if(flag){
printf("2\n");
for(int i=1; i<=m; i++){
printf("%d ",ans[i]);
}
}
else{
printf("1\n");
for(int i=1; i<=m; i++){
printf("%d ",1);
}
}
return 0;
}
需要事先判断一下是否有环,如果原图没有环显然全部输出 \(1\) 就好了。注意这个图建生成树可能是一个森林。
E.Sum Queries?
题目描述:
题目分析:
我们会发现如果一个序列是好的,那么就意味着不存在在某一个数位上,有两个数都有值。因为如果有值加起来这一位也不一定有,进位之后也就会对下一位产生影响,也就一直影响也就不可能和合法了。
那么对于一个坏的序列,也就意味着一定存在某个数位有两个或以上的数都有值,我们的不合法也就是针对某一个数位而言的,而对于这个数位如果不满足那么最小的和也就是这个数位上有值的数里面的最小值与次小值的和,显然如果加入第三个数就不如两个数更优了,而且这两个数由于某一位上都有值所以也一定是不合法。
那么问题也就是:对于每一个数位我们寻找这一个数位上有值的数里的最小值与次小值的和,而且还要支持区间查询。
所以很显然对于每一位建一棵线段树就好了,询问就是区间查询最小值和次小值。
代码详解:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const long long MAXN = 2e5+10;
const long long INF = INT_MAX;
struct node{
long long min,secmin;
node(){}
node(long long _min,long long _secmin){
min = _min,secmin = _secmin;
}
};
long long b[MAXN],a[MAXN];
struct Segment_tree{
long long mn[4 * MAXN],secmn[4 * MAXN];
void update(long long now){
mn[now] = mn[now<<1],secmn[now] = secmn[now<<1];
if(secmn[now<<1|1] < secmn[now]) secmn[now] = secmn[now<<1|1];
if(secmn[now] < mn[now]) swap(mn[now],secmn[now]);
if(mn[now<<1|1] < secmn[now]) secmn[now] = mn[now<<1|1];
if(secmn[now] < mn[now]) swap(mn[now],secmn[now]);
}
void build_tree(long long now,long long now_l,long long now_r){
mn[now] = secmn[now] = INF;
if(now_l == now_r){
if(b[now_l]) mn[now] = a[now_l];
return;
}
long long mid = (now_l + now_r)>>1;
build_tree(now<<1,now_l,mid);
build_tree(now<<1|1,mid+1,now_r);
update(now);
}
void change(long long now,long long now_l,long long now_r,long long pos,long long k,long long x){
if(now_l == now_r){
mn[now] = secmn[now] = INF;
if(k) mn[now] = x;
return;
}
long long mid = (now_l+now_r)>>1;
if(pos <= mid) change(now<<1,now_l,mid,pos,k,x);
if(pos > mid) change(now<<1|1,mid+1,now_r,pos,k,x);
update(now);
}
node merge(node l,node r){
node ans = l;
if(r.secmin < ans.secmin) ans.secmin = r.secmin;
if(ans.secmin < ans.min) swap(ans.min,ans.secmin);
if(r.min < ans.secmin) ans.secmin = r.min;
if(ans.secmin < ans.min) swap(ans.min,ans.secmin);
return ans;
}
node query(long long now,long long now_l,long long now_r,long long l,long long r){
if(l <= now_l && r >= now_r){
return node(mn[now],secmn[now]);
}
long long mid = (now_l + now_r)>>1;
node ans;
bool flag = false;
if(l <= mid){
ans = query(now<<1,now_l,mid,l,r);
flag = true;
}
if(r > mid){
if(!flag)
ans = query(now<<1|1,mid+1,now_r,l,r);
else
ans = merge(ans,query(now<<1|1,mid+1,now_r,l,r));
}
return ans;
}
}tree[10];
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
long long n,q;
// cin>>n>>q;
scanf("%lld%lld",&n,&q);
for(long long i=1; i<=n; i++){
// cin>>a[i];
scanf("%lld",&a[i]);
}
long long res = 1;
for(long long i=0; i<9; i++){
for(long long j=1; j<=n; j++){
b[j] = (a[j] / res) % 10;
}
tree[i].build_tree(1,1,n);
res *= 10;
}
while(q--){
long long opt,x,y;
// cin>>opt>>x>>y;
scanf("%lld%lld%lld",&opt,&x,&y);
if(opt == 1){
for(long long i=0; i<9; i++){
tree[i].change(1,1,n,x,0,0); //改成INF
}
a[x] = y;
for(long long i=0; i<9; i++){
tree[i].change(1,1,n,x,y%10,a[x]);
y/=10;
}
}
else if(opt == 2){
long long ans = INF; //注意用 INT_MAX
for(long long i=0; i<9; i++){
node tmp = tree[i].query(1,1,n,x,y);
ans = min(ans,((tmp.secmin == INF) ? INF : (tmp.min + tmp.secmin))); //不能无脑加,可能直接爆了
}
if(ans >= INF) printf("-1\n");
else printf("%lld\n",ans);
}
}
return 0;
}