Educational Codeforces Round #67
这一场的D题真的好惨烈.......FST了几百人qwq.........
题目链接:戳我
A Stickers and Toys
一个egg里面有可能仅有toy,或者仅有sticker,或者两者都有.给你egg,toy,sticker的数量,你不知道具体每个egg里面的情况.现在问你最坏情况下至少要砸开多少个egg,才能获得至少一个toy和一个sticker.
抽屉原理?没啥可说的.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define MAXN 200010
using namespace std;
int n,m,T,a,b;
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&a,&b);
int ans=max(n-a+1,n-b+1);
cout<<ans<<endl;
}
return 0;
}
B Letters Shop
给你一个字符串s,然后以及若干询问(字符串t),问每次需要至少取s的多长前缀,重新组合之后可以拼成t.
随便写一下就行了吧QAQ
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define MAXN 200010
using namespace std;
int n,m;
int cnt[26],pos[26][MAXN],cur_cnt[26];
char s[MAXN],cur[MAXN];
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d",&n);
scanf("%s",s+1);
for(int i=1;i<=n;i++)
{
cnt[s[i]-'a']++;
pos[s[i]-'a'][cnt[s[i]-'a']]=i;
}
scanf("%d",&m);
for(int p=1;p<=m;p++)
{
scanf("%s",cur+1);
int len=strlen(cur+1),ans=0;
memset(cur_cnt,0,sizeof(cur_cnt));
for(int i=1;i<=len;i++)
{
cur_cnt[cur[i]-'a']++;
ans=max(ans,pos[cur[i]-'a'][cur_cnt[cur[i]-'a']]);
}
printf("%d\n",ans);
}
return 0;
}
C Vasya And Array
给定你一些要求,t=1时,要求给定区间内的序列单调不降,t=0时,要求给定区间内的序列至少存在一组\(a_i,a_j\)满足\(i<j\)且\(a_i>a_j\).让你构造序列.
我们对整个序列做一个差分.先把单调不降的区间拉出来讨论,单调不降的话我们可以让他们相等.
然后再处理t=0的情况.....我们可以选择区间内任意一个不需要和前面相等的数,使得他的差分值为-1.如果找不到这个数的话,那么就构造不出来这个序列了QAQ,返回false即可.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define MAXN 200010
#define inf 0x3f3f3f3f
using namespace std;
int n,m;
int a[MAXN],done[MAXN],ans[MAXN];
struct Node{int op,l,r;}node[MAXN];
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&node[i].op,&node[i].l,&node[i].r);
}
for(int i=2;i<=n;i++) a[i]=-inf;
for(int i=1;i<=m;i++)
{
if(node[i].op==0) continue;
for(int j=node[i].l+1;j<=node[i].r;j++) a[j]=0;
}
// for(int i=1;i<=n;i++) printf("%d ",a[i]); puts("");
for(int i=1;i<=m;i++)
{
if(node[i].op==1) continue;
bool flag=false;
for(int j=node[i].l+1;j<=node[i].r;j++)
{
if(a[j]==-1)
{
flag=true;
break;
}
if(a[j]==-inf)
{
a[j]=-1;
flag=true;
break;
}
}
if(flag==false)
{
cout<<"NO"<<endl;
return 0;
}
}
// for(int i=1;i<=n;i++) printf("%d ",a[i]); puts("");
cout<<"YES"<<endl;
for(int i=1;i<=n;i++) if(a[i]==-inf) a[i]=0;
for(int i=1;i<=n;i++) ans[i]=ans[i-1]+a[i];
int minn=0;
for(int i=1;i<=n;i++) minn=min(minn,ans[i]);
for(int i=1;i<=n;i++) ans[i]+=-minn+1;
for(int i=1;i<=n;i++) printf("%d ",ans[i]); puts("");
return 0;
}
D Subarray Sorting
最最惨烈的一道题,赛时看着好多人都过了.然后大量地FST.........
给你两个序列,A和B,每次可以任意选定区间进行sort(排序默认从小到大),问能否从A操作成B.
赛时想的思路是,只要两个序列里面的数都是一样的,因为大的肯定不能跑到小的前面,所以只要A的字典序大于B就行了.
但是很容易被叉掉......比如说A=2 6 3 7,B=2 3 7 6
考虑正解:
我们对A建一个线段树,维护区间的最小值.
然后我们考虑把B一个一个放进去,比如说当前处理的是B[i],这个数在A里面的出现位置为v.我们求[1,v]的最小值,如果最小值小于b[i],那么就不可能,返回false.否则我们更改v为一个很大的数(根据题意a[i],b[i]<=n,所以改成n+1即可)
为什么这样子是对的?
因为如果在放b[i]的时候,如果前面的最小值为a[j],它在B中的位置为k,那么显然j<->k的连线会与v<->i相交,而根据sort的原理,小的数是不能跑到大的数的后面的,所以不合法.我们修改完之后,相当于在这个位置已经确定可以放B的数了,所以改为n+1以免对后面的操作产生影响.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define MAXN 300010
using namespace std;
int n,T;
int a[MAXN],b[MAXN],minn[MAXN<<2],cnt[MAXN];
vector<int>pos[MAXN];
inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
inline void push_up(int x){minn[x]=min(minn[ls(x)],minn[rs(x)]);}
inline void build(int x,int l,int r)
{
if(l==r) {minn[x]=a[l];return;}
int mid=(l+r)>>1;
build(ls(x),l,mid);
build(rs(x),mid+1,r);
push_up(x);
}
inline void update(int x,int l,int r,int pos,int k)
{
if(l==r)
{
minn[x]=k;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) update(ls(x),l,mid,pos,k);
else update(rs(x),mid+1,r,pos,k);
push_up(x);
}
inline int query(int x,int l,int r,int ll,int rr)
{
if(ll<=l&&r<=rr) return minn[x];
int mid=(l+r)>>1,cur_ans=0x3f3f3f3f;
if(ll<=mid) cur_ans=min(cur_ans,query(ls(x),l,mid,ll,rr));
if(mid<rr) cur_ans=min(cur_ans,query(rs(x),mid+1,r,ll,rr));
return cur_ans;
}
inline bool solve()
{
for(int i=1;i<=n;i++)
{
int siz=(int)pos[b[i]].size();
if(siz<=cnt[b[i]]) return false;
int now=pos[b[i]][cnt[b[i]]++];
if(query(1,1,n,1,now)<b[i]) return false;
update(1,1,n,now,n+1);
}
return true;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) pos[i].clear(),cnt[i]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[a[i]].push_back(i);
}
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
build(1,1,n);
if(solve()==true) printf("YES\n");
else printf("NO\n");
}
return 0;
}
E Tree Painting
简化版题意:任意选取一个点为根,求最大的子树大小和.
感觉有点像换根DP,先来一遍dfs求出来以(比如说1)为根的子树大小和,然后你再来一个dfs换根,就好了
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define MAXN 200010
using namespace std;
int n,t;
int head[MAXN],siz[MAXN];
long long ans;
struct Edge{int nxt,to;}edge[MAXN<<1];
inline void add(int from,int to)
{
edge[++t].nxt=head[from],edge[t].to=to;
head[from]=t;
}
inline void dfs(int x,int pre)
{
siz[x]=1;
for(int i=head[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v==pre) continue;
dfs(v,x);
siz[x]+=siz[v];
// printf("[%d %d] %d\n",x,v,siz[x]);
}
}
inline void change_root(int x,int pre,long long w)
{
ans=max(ans,w);
for(int i=head[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v==pre) continue;
change_root(v,x,(w-siz[v])+(n-siz[v]));
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dfs(1,0);
// for(int i=1;i<=n;i++)
// printf("siz[%d]=%d\n",i,siz[i]);
for(int i=1;i<=n;i++) ans+=siz[i];
// printf("ans=%lld\n",ans);
change_root(1,0,ans);
printf("%lld\n",ans);
return 0;
}
然后后面的题本菜鸡还不会写.........
先咕着吧.......
(唔,最后感谢一下赛时给我提供题意BLUESKY007神仙qwq)