2018US Open Contest gold 题解

T1

题目大意:给定一个序列,求鸡尾酒排序需要进行多少次原序列有序
n &lt; = 1 0 6 , a i &lt; = 1 0 9 n&lt;=10^6,a_i&lt;=10^9 n<=106,ai<=109


题解:
考察的是对冒泡排序以及鸡尾酒排序原理的理解
模拟考试的时候还是卡了一会的
一个比较显然的推论:
我们确定一个分界线,对于前面的数中所有满足序列有序后在分界线后的点,每次鸡尾酒排序都至少有一个数字会被换到后面。反向也是同理。
然后只要统计所有分界线这个值得最大值即可

排序后依次插入位置,树状数组维护即可

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
int rd()
{
	int num = 0;char c = getchar();bool flag = true;
	while(c < '0'||c > '9') {if(c == '-') flag = false;c = getchar();}
	while(c >= '0' && c <= '9') num = num*10+c-48,c = getchar();
	if(flag) return num;else return -num;
}
int tr[1010000];
int n;
P a[1010000];
void add(int x,int v){for(int i=x;i<=n;i+=i&-i) tr[i]+=v;}
int query(int x){int sum=0;for(int i=x;i;i-=i&-i) sum+=tr[i];return sum;}
#define fr first
#define se second
int main()
{  
    freopen("sort.in","r",stdin);
    freopen("sort.out","w",stdout);
    n = rd();rep(i,1,n) a[i].fr = rd(),a[i].se = i;
    sort(a+1,a+n+1);
    int ans = 1;
	rep(i,1,n-1)
    {
    	add(a[i].se,1);
    	ans = max(ans,i-query(i));
    }
    printf("%d\n",ans);
	return 0;
}

T2

题目大意:
给定n个点和m组限制
每组限制相当于是连一些有向边
求满足前x组限制的条件下字典序最小的拓扑序
满足限制的意思是无环
前提是x最大
n &lt; = 1 0 5 , m &lt; = 5 ∗ 1 0 4 , ∑ m i &lt; = 2 ∗ 1 0 6 n&lt;=10^5,m&lt;=5*10^4,\sum m_i &lt;= 2*10^6 n<=105,m<=5104,mi<=2106


题解:
显然答案具有单调性
每次二分,用拓扑序判断
最后输出的时候用单调队列即可

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
int rd()
{
	int num = 0;char c = getchar();bool flag = true;
	while(c < '0'||c > '9') {if(c == '-') flag = false;c = getchar();}
	while(c >= '0' && c <= '9') num = num*10+c-48,c = getchar();
	if(flag) return num;else return -num;
}
int n,m,linkk[101000],t;
struct node{int n,y,tim;}e[201000];
int tmp[201000],ans1;
bool vis[201000];
void insert(int x,int y,int z){e[++t].y = y;e[t].n = linkk[x];e[t].tim = z;linkk[x] = t;} 
int du[101000];
struct cmp{
	bool operator ()(int a,int b) {
		return a>b;
	}
};
priority_queue<int,vector<int>,cmp>q;
void tpsort()
{
	rep(i,1,t) if(e[i].tim <= ans1) du[e[i].y]++;
	rep(i,1,n) if(!du[i]) q.push(i);
	int num = 0;
	while(!q.empty())
	{
		int x = q.top();num++;q.pop();
		if(num != n)printf("%d ",x);else printf("%d",x);
		rept(i,x)
		    if(e[i].tim <= ans1)
		    {
		    	int y = e[i].y;
		    	du[y]--;
		    	if(du[y] == 0) q.push(y);
		    }
	}
}
queue<int>Q;
bool check(int mid)
{
	rep(i,1,n) du[i] = 0;
	rep(i,1,t) if(e[i].tim <= mid) du[e[i].y]++;
	rep(i,1,n) if(!du[i]) Q.push(i);
	while(!Q.empty())
	{
		int x = Q.front();Q.pop();
		rept(i,x)
		    if(e[i].tim <= mid)
			{
				int y = e[i].y;
				du[y]--;
				if(du[y] == 0) Q.push(y);
			} 
	}
	rep(i,1,n) if(du[i] != 0)  return false;
	return true;
}
void work()
{
	int l = 1,r = m;
	while(l+1<r)
	{
		int mid = (l+r)>>1;
		rep(i,1,n) vis[i] = false;
		if(check(mid)) l = mid;
		else r = mid;
	}
	rep(i,1,n) vis[i] = false;
	if(check(r)) ans1 = r;
	else
	{   
		rep(i,1,n) vis[i] = false;
		if(check(l)) ans1 = l;
		else ans1 = 0; 
	}
	if(ans1>0) tpsort();
	else {rep(i,1,n-1) printf("%d ",i);printf("%d",n);}
}
int main()
{
	freopen("milkorder.in","r",stdin);
	freopen("milkorder.out","w",stdout);
	n = rd();m = rd();
	rep(i,1,m)
	{
		int x = rd();
		rep(j,1,x) 	tmp[j] = rd();
	    rep(j,1,x-1) insert(tmp[j],tmp[j+1],i);
	}
	work();
	return 0;
}

T3

题目大意:
给定 n n n个二元组 ( v , w ) (v,w) (v,w)和一个正整数 W W W
要求调出若干个二元组,在满足 ∑ w i &gt; = W \sum w_i &gt;= W wi>=W的条件下最大化 ∑ v i ∑ w i \frac{\sum v_i}{\sum w_i} wivi
1 &lt; = n &lt; = 250 , 1 &lt; = W &lt; = 1 0 3 , 1 &lt; = w i &lt; = 1 0 6 , 1 &lt; = v i &lt; = 1 0 3 1&lt;=n&lt;=250,1&lt;=W&lt;=10^3,1&lt;=w_i&lt;=10^6,1&lt;=v_i&lt;=10^3 1<=n<=250,1<=W<=103,1<=wi<=106,1<=vi<=103
如果答案为实数x,要求输出 ⌊ 1000 x ⌋ \lfloor 1000x \rfloor 1000x


题解:
0/1分数规划模板题
用实数二分可能会有精度问题,当然卡的好还是能过去
题解给的做法是我们在 c h e c k check check的时候由于输出的要求,我们可以都乘上 1000 1000 1000的系数,然后用 0 / 1 0/1 0/1背包直接做

太水了没写代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值