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)

posted @ 2019-07-02 11:47  风浔凌  阅读(274)  评论(2编辑  收藏  举报