Educational Codeforces Round 67(A-E) 题解
A. Stickers and Toys
- 简单的解方程和鸽巢原理
code
int main(){
cin>>T;
while(T--){
cin>>a>>b>>c;
int z = (b+c)-a;
int x = b-z;
int y = c-z;
cout<
B. Letters Shop
- 给出串s1,s2;求在s1中前k位能出现串s2(字符频次相同)的最小k
- 可以记录字符i依次出现的位置,然后仅从从s2中考虑max()
code
#include
using namespace std;
const int maxn = 2e5+10;
vector mp[26];
char st[maxn],p[maxn];
int cnt[26];
int n,m;
int solve(){
int lp = strlen(p);
int maxv = 0;
memset(cnt,0,sizeof(cnt));
for(int i=0;i
C. Vasya And Array
- 被hack的重灾区,在于很容易写出有漏洞的代码
- 核心思路:递增区间构造为元素相等,整个区间整体下降,非常贪心的保持无序
- 但在检查t==0情况是否合理时,并不是都在递增区间就不合理;所以递增区间只标记\(l...r-1\);t=0时,落在这个区域的是不合理的
code
#include
#define rep(i,n) for(int i=0;i>query;
int main(){
cin>>n>>m;
for(int i=0;i>t>>a>>b;
if(t == 1){
for(int j=a;j
D. Subarray Sorting
- 数组a1,a2;询问数组a1能否对任意区间[l,r]进行任意次排序(可以选择很多次区间)转变成a2
- 很巧妙的线段树思路;维护区间最小值,假设处理都a2[i]=t,a2[i]在a1中对应的pos = m;那么要求[1..pos]最小值为a2[i];然后更新pos的元素为Inf
- 这样意味着[1..pos]进行重排肯定能与当前a2相同
const int maxn = 3e5+100;
const int inf = 0x1f1f1f1f;
int a[maxn],b[maxn];
int aa[maxn],bb[maxn];
int seg[maxn<<2];//线段树
int t,n;
void build(int ro,int l,int r){
if(l==r){seg[ro] = a[l];return ;}
int mid = (l+r)>>1;
build(ro<<1,l,mid);
build(ro<<1|1,mid+1,r);
seg[ro] = min(seg[ro<<1],seg[ro<<1|1]);
}
void update(int ro,int l,int r,int pos,int x){
if(l==r&&l==pos){
seg[ro] = x;
return ;
}
int mid = (l+r)>>1;
if(pos<=mid) update(ro<<1,l,mid,pos,x);
else update(ro<<1|1,mid+1,r,pos,x);
seg[ro] = min(seg[ro<<1],seg[ro<<1|1]);
}
int query(int ro,int l,int r,int a,int b){
if(a<=l&&r<=b){
return seg[ro];
}
int mid = (l+r)>>1;
int minv = inf;
if(a<=mid) minv = min(minv,query(ro<<1,l,mid,a,b));
if(b>mid) minv = min(minv,query(ro<<1|1,mid+1,r,a,b));
return minv;
}
queue g[maxn];//存储g[i] 存储元素i的位置
int main(){
ios_base::sync_with_stdio(false);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){cin>>a[i];aa[i] = a[i];while(g[i].size()) g[i].pop();}
for(int i=1;i<=n;i++){cin>>b[i];bb[i] = b[i];}
sort(aa+1,aa+1+n);sort(bb+1,bb+1+n);
bool OK = true;
for(int i=1;i<=n;i++){
if(aa[i]!=bb[i]) {cout<<"NO\n";OK=false;break;}
}
if(!OK) continue;
//清空队列
for(int i=1;i<=n;i++){
g[a[i]].push(i);//存储元素位置
}
//memset(seg,inf,sizeof(seg));
build(1,1,n);//建立线段树
bool flag = true;
for(int i=1;i<=n;i++){
int s = g[b[i]].front(); g[b[i]].pop();
int k = query(1,1,n,1,s);
//db(b[i]);db(k);
if(k!=b[i]){
cout<<"NO\n";flag = false; break;
}
else{
update(1,1,n,s,inf);
}
}
if(flag) cout<<"YES\n";
}
}
E. Tree Painting
- 题意:每次选择黑点子集相邻的白点点进行染色,增加的points为该白点所在连通分量的节点数
- 先任选节点进行区间dp,然后换根,并考虑换根的影响
code
//树上dp 然后换根
#include
using namespace std;
#define ll long long
#define db(x) cout<<"["<<#x<<"]="< g[maxn];
ll dp[maxn];//最开始 dp[]处理某一点为根的树的结果(包括子树)
//以任意点为根,dfs的最大点是一样的
//然后考虑换根影响
ll sz[maxn];//子树大小
int n;
void dfs(int v,int fa = 0){//以V为根节点
sz[v] =1;
dp[v]=0;
for(int t : g[v]){
if(t==fa) continue;
dfs(t,v);
sz[v]+=sz[t];
dp[v]+=dp[t];
}
dp[v]+=sz[v];
}
void solve(int v,int fa = 0){//换根
for(int t:g[v]){
if(t==fa) continue;
dp[t] = dp[v]-2*sz[t]+n;
solve(t,v);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i