usaco 2018 open contest gold

第二题比较简单,第三题推导有点难,第一题结论比较好猜,但证明是看题解才有点明白。题解的证明没看懂,所以自己想出来一个思路。
A:
题意:类似于双向冒泡排序的一种方法:从1到n循环,每次如果后面的数比前面的数小就交换相邻的那两个数;再从n到1循环一次。从1到n加上从n到1总共算1次操作,问一个可能有相同的数列一共要几次才能从小到大排序。题目中还有一个想不到的问题就是及时已经排好序也要操作一次。注意“循环”不是“操作”。
思路:首先可以把每个循环认为是把一个区间的第一个数值也就是区间最大值移到区间最后,或相反。把数列在i和i+1直接画一条线,把数列分成2段。这时会有一些数在原数组的左边出现过,但在排好序的数组左边没出现(即在排好序的数组右边出现了,就是操作后跑到目标数组的右半边去了)。显然在原数组的右边出现过,但在排好序的数组右边没出现的数的数量和上一个的数量数量是一样的设这个数是mi。此时mi的最大值max(mi)就是答案。证明:显然max是上界,现在证明它是下界,先证明每个mi都是下界,也就是证明每一次操作最多能使每个mi减少1:如果此时考虑i和i+1直接的线,当右边的数有mi个和排好序的右边不同,如果第一次从1到n循环使一个“应该”(排序后)在右边的数到了右面,(它到右边会把右半部分最左边的一个数即第i+1个数“挤”到左边)且挤出的那个数是“应该”在右边的,那么有一个进来又有一个出去,mi就没有变,再从n到1循环时至多使mi减少1,这次操作最多使mi减少1。如果挤到左面的数不“应该”是右面的,那么mi会减1,但从n到1时可能把右边的一个数移到左面去,这时挤到右面的那个数一定是刚刚被挤到左边的那个数,就是说这次循环没有改变mi。如果第一个循环没有把一个数移过线,那么mi不变,mi变化还是取决于下一次循环。综上,每次操作最多把每个mi减少1,所以mi也是下界。实现可以树状数组,也可以离散化(笨一些)。

B:
二分+拓扑
带字典序最小的拓扑用人肉法很好用。

C:
二分答案。考虑x行不行的时候直接是 ∑ i ∈ S t i ∑ i ∈ S w i ≥ x \frac{\sum_{i∈S}ti}{\sum_{i∈S}wi}≥x iSwiiStix。就是 ∑ i ∈ S ( T i − x ∗ w i ) ≥ 0 \sum_{i∈S}{(Ti−x*wi)}≥0 iS(Tixwi)0。算出每个的 ( t i − x ∗ w i ) (ti−x*wi) tixwi作为贡献值,dp一下能不能达到 ∑ i ∈ S w i ≥ w \sum_{i∈S}wi≥w iSwiw时S中的贡献值和是非负的。因为要乘1000后保留整数,所以可以x乘1000并且把贡献值变成 ( 1000 ∗ t i − x ∗ w i ) (1000*ti-x*wi) 1000tixwi这样二分的就是一个整数。

A:

#include<cstdio>
#include<string>
#include<cstring>
#include<utility>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#include<algorithm>
#include<vector>
#include<iostream>
#define ll long long
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define inf 0x7fffffff
#define minn(x,y) x=min(x,y)
#define maxx(x,y) x=max(x,y)
using namespace std;
int a[100010],b[100010],c1[100010],c2[100010];
map<int,int> ma;
int main()
{
	int i,j,k,n,m,x,y,z;
	freopen("sort.in","r",stdin);freopen("sort.out","w",stdout);
	scanf("%d",&n);
	for(i=0;i<n;i++)
	{
		scanf("%d",&a[i]);
		b[i]=a[i];
	}
	sort(b,b+n);
	k=0;
	ma[b[0]]=0;
	for(i=1;i<n;i++)
	{
		if(b[i-1]!=b[i])
		{
			k++;
			ma[b[i]]=k;
		}
	}
	int ans=0,sum=0;
	for(i=0;i<n;i++)
	{
		x=ma[a[i]];
		y=ma[b[i]];
		if(x==y)
		{
			sum++;
			ans=max(ans,i+1-sum);
			continue;
		}
		c1[x]++;
		c2[y]++;
		if(c1[y])
		{
			c1[y]--;
			sum++;
		}
		if(c2[x])
		{
			c2[x]--;
			sum++;
		}
		ans=max(ans,i+1-sum);
	}
	if(ans==0)
	{
		ans=1;
	}
	printf("%d",ans);
}

B:

#include<cstdio>
#include<string>
#include<cstring>
#include<utility>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#include<algorithm>
#include<vector>
#include<iostream>
#define ll long long
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define inf 0x7fffffff
#define minn(x,y) x=min(x,y)
#define maxx(x,y) x=max(x,y)
using namespace std;
int a[100010];
vector<pii> ve[100010];
set<pii> s;
int ma,n;
bool vis[100010];
bool st[100010];
int dfs(int x)
{
	int i,to;
	vis[x]=1;
	st[x]=1;
	for(i=0;i<ve[x].size();i++)
	{
		if(ma<ve[x][i].se)
		{
			continue;
		}
		to=ve[x][i].fi;
		if(!vis[to])
		{
			if(!dfs(to))
			{
				st[x]=0;
				return 0;
			}
		}
		else if(st[to])
		{
			st[x]=0;
			return 0;
		}
	}
	st[x]=0;
	return 1;
}
bool chk(int x)
{
	ma=x;
	memset(vis,0,sizeof(vis));
	for(int i=0;i<n;i++)
	{
		if(!vis[i])
		{
			if(!dfs(i))
			{
				return 0;
			}
		}
	}
	return 1;
}
vector<int> ans;
int in[100010];
int main()
{
	int i,j,k,m,x,y,z;
	freopen("milkorder.in","r",stdin);
	freopen("milkorder.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(i=0;i<m;i++)
	{
		scanf("%d",&k);
		for(j=0;j<k;j++)
		{
			scanf("%d",&a[j]);
			a[j]--;
		}
		for(j=1;j<k;j++)
		{
			if(!s.count(mp(a[j-1],a[j])))
			{
				ve[a[j-1]].push_back(mp(a[j],i));
				s.insert(mp(a[j-1],a[j]));
			}
		}
	}
	int l=0,r=m,mid;
	while(r-l>1)
	{
		mid=(l+r)/2;
		if(chk(mid))
		{
			l=mid;
		}
		else
		{
			r=mid;
		}
	}
	for(i=0;i<n;i++)
	{
		sort(ve[i].rbegin(),ve[i].rend());
	}
	priority_queue<int> q;
	for(i=0;i<n;i++)
	{
		for(j=0;j<ve[i].size();j++)
		{
			if(ve[i][j].se>l)
			{
				continue;
			}
			in[ve[i][j].fi]++;
		}
	}
	for(i=0;i<n;i++)
	{
		if(in[i]==0)
		{
			q.push(-i);
		}
	}
	while(!q.empty())
	{
		x=-q.top();
		q.pop();
		ans.push_back(x);
		for(i=0;i<ve[x].size();i++)
		{
			if(ve[x][i].se>l)
			{
				continue;
			}
			in[ve[x][i].fi]--;
			if(in[ve[x][i].fi]==0)
			{
				q.push(-ve[x][i].fi);
			}
		}
	}
	for(i=0;i<n;i++)
	{
		if(i>0)
		{
			printf(" ");
		}
		printf("%d",ans[i]+1);
	}
}

C:

#include<cstdio>
#include<string>
#include<cstring>
#include<utility>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#include<algorithm>
#include<vector>
#include<iostream>
#define ll long long
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define inf 0x7fffffff
#define minn(x,y) x=min(x,y)
#define maxx(x,y) x=max(x,y)
using namespace std;
int a[300],b[300];
ll c[300];
ll dp[1010];
int main()
{
	int i,j,k,n,m,x,y,z;
	freopen("talent.in","r",stdin);
	freopen("talent.out","w",stdout);
	scanf("%d%d",&n,&m);
	int l=0,r=0;
	for(i=0;i<n;i++)
	{
		scanf("%d%d",&a[i],&b[i]);
		r=max(r,b[i]*1000/a[i]);
	}
	r+=5;
	int mid;
	while(l+1<r)
	{
		mid=(l+r)/2;
		for(i=0;i<n;i++)
		{
			c[i]=1000ll*b[i]-1ll*mid*a[i];
		}
		dp[0]=0;
		for(i=1;i<=m;i++)
		{
			dp[i]=-inf;
		}
		for(i=0;i<n;i++)
		{
			for(j=m;j>=0;j--)
			{
				if(dp[j]!=-inf)
				{
					dp[min(m,j+a[i])]=max(dp[min(m,j+a[i])],dp[j]+c[i]);
				}
			}
		}
		if(dp[m]>=0ll)
		{
			l=mid;
		}
		else
		{
			r=mid;
		}
	}
	printf("%d",l);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值