AtCoder Beginner Contest 285 题解

D

大意:

n个人,每一个人有一个初始值和一个想要修改的值,两者保证不同。每次只能修改一个人的值,且不能与其他人的值重复。问能否满足所有人的要求

思路:

显然,判环即可

code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
ll n;
map<string,ll> mp;
struct ty
{
	string a,b;
}mas[N];
ll ne[N];
ll vis[N];
ll cn=0;
void dfs(ll id)
{
	vis[id]=cn;
	ll nex=ne[id];
	if(nex==0) return;
	if(vis[nex]!=cn) dfs(nex);
	else 
	{
		cout<<"No"<<endl;
		exit(0);
	}
} 
void solve()
{
	cin>>n;
	for(int i=1;i<=n;++i)
	{
		cin>>mas[i].a>>mas[i].b;
		mp[mas[i].a]=i;
	}
	for(int i=1;i<=n;++i)
	{
		ll id=mp[mas[i].b];
		ne[i]=id;
	}
	for(int i=1;i<=n;++i)
	{
//		memxet(vis,0,sizeof vis);
		if(!vis[i]) cn++,dfs(i);	
	} 
	cout<<"Yes"<<endl;
}
																																																																																																																																																																																																																																																																																																												                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	solve();
	return 0;
}

E

大意:

一周有n天,给出一个数组a,现在设计这一周的休息日的安排(至少一个),并计算每周的价值。每天的价值:休息日没价值,工作日i的价值为a[min(i1,i2)],i1表示i至少往前几天有休息日,i2表示i至少往后几天有休息日,n<5000

思路:

气死我啦气死我啦,一心当成一周七天来算,然后写了个状压。。。

会发现如果考虑如何安排的话,我们要关注休息日的数量和分布,这是很麻烦的

所以我们dp(只有玄学可以压制玄学)

不妨设dp[i]表示前i天中第i天恰好为休息日的最大价值。则

dp[i]=max(dp[i],dp[j]+co(i-j-1));//1<=j<i,co(x)代表区间长度为x,且两端(不包含)都是休息日的总价值

显然co可以预处理然后O(1)解决

还有一个细节就是第n天不一定是休息日最优。注意到一周是固定的,我们不妨转一转,将某个休息日作为第一天(显然不会影响最终结果),那么答案就是dp[n+1]了

code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
ll n;
ll mas[N];
ll sum[N];
ll co(ll x)
{
	return sum[(x+1)/2]+sum[x/2];
}
ll dp[N];
void solve()
{
	cin>>n;
	for(int i=1;i<=n;++i) cin>>mas[i],sum[i]=sum[i-1]+mas[i];
	dp[1]=0;
	for(int i=1;i<=n+1;++i)
	{
		for(int j=1;j<i;++j)
		{
			dp[i]=max(dp[i],dp[j]+co(i-j-1));	
		}	
	}	
	cout<<dp[n+1]<<endl;
}
																																																																																																																																																																																																																																																																																																												                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	solve();
	return 0;
}

F

大意:

给定一个字符串s,提供两种操作:
1 修改某个位置的字符

2 给定l,r,判断区间l,r对应的子串是否是s排序后的新字符串t的子串

思路:

考虑一下第二个操作,如果想要答案为真,我们需要满足以下两个条件:

区间中出现的字母一定是按顺序出现的,这个显然

以及中间(不包括两端)的字母在[l,r]出现的次数=在s中出现的总次数,否则s排序后一定会有新字母加进来

这是我们需要的两个条件

不难发现第二个条件是很好维护的,开26个树状数组就行了。那么对于第一个条件,只要我们在check的时候按字母顺序去跑,并更新当前位置,就行了。

tip:同一个位置可能多次修改,所以操作一弄完后对原字符串也要更新一下(应该只有我会漏过这个细节吧。。。)

code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
ll n,q;
char s[N];
ll a,b,c;
ll l,r;
char d;
struct ty
{
	ll tr[N];
	ll low(ll x)
	{
		return x&(-x);
	}
	void add(ll x,ll y)
	{
		while(x<=n)
		{
			tr[x]+=y;
			x+=low(x);
		}
	}
	ll sum(ll x)
	{
		ll ans=0;
		while(x)
		{
			ans+=tr[x];
			x-=low(x); 
		}
		return ans;
	}
	ll get_sum(ll l,ll r)
	{//返回区间字母个数 
		return sum(r)-sum(l-1);
	}
	
}tre[27]; 

void sol()
{
	cin>>a;
	if(a==1)
	{
		cin>>b>>d;
		tre[s[b]-'a'].add(b,-1);
		s[b]=d;
		tre[d-'a'].add(b,1);
		return;
	}
	cin>>l>>r;
	
	int ma=0,mi=26;
	for(int i=0;i<26;++i)
	{
		if(tre[i].get_sum(l,r)>0) 
		{
			ma=max(ma,i);
			mi=min(mi,i);
		}
	}
//	cout<<" sf "<<mi<<' '<<ma<<endl;
	//有就先塞进来 
	ll x=l;
	for(int i=mi;i<=ma;++i)
	{
		//中间的数字
	 	//一个要求是其所有都在区间内出现
	 	ll det=tre[i].get_sum(l,r);
		if(i!=mi&&i!=ma)
		{
		    if(det<tre[i].get_sum(1,n))
		    {
		 		cout<<"No"<<endl;
		 		return;
		    } 
		    
		}
		if(tre[i].get_sum(x,x+det-1)!=det)
	    {
	    	cout<<"No"<<endl;
	    	return;
		}
		x+=det;
	}
	cout<<"Yes"<<endl;
}
void solve()
{
	cin>>n;
	cin>>(s+1);
	cin>>q;
	for(int i=1;i<=n;++i)
	{
		tre[s[i]-'a'].add(i,1); 
	}
//	for(int i=0;i<26;++i)
//	{
//		cout<<(char)('a'+i)<<' '<<tre[i].get_sum(1,3)<<endl;
//	}
	while(q--)
	{
		sol(); 
	}
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	solve();
	return 0;
}

G

大意:

给定一个图,有两种拼图:1*1,1*2,图中的1代表该位置是一个1*1的拼图,2代表1*2的拼图,?代表随便。现在问该图是否有合法的还原(不同拼图不能重叠)

思路:

标1的位置显然不用管。只考虑剩下的点,并将相邻点连边的话,显然是一张二分图,我们的目标其实就是要使得每一个标2的位置有一个匹配,剩下的?位置填1就行了。

对二分图跑网络流的话,就是要让有2的路径流量为1

那么最大流就好了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值