1. 程式人生 > >2018US Open Contest gold 題解

2018US Open Contest gold 題解

T1

題目大意:給定一個序列,求雞尾酒排序需要進行多少次原序列有序 n<=106,ai<=109n<=10^6,a_i<=10^9

題解: 考察的是對氣泡排序以及雞尾酒排序原理的理解 模擬考試的時候還是卡了一會的 一個比較顯然的推論: 我們確定一個分界線,對於前面的數中所有滿足序列有序後在分界線後的點,每次雞尾酒排序都至少有一個數字會被換到後面。反向也是同理。 然後只要統計所有分界線這個值得最大值即可

排序後依次插入位置,樹狀陣列維護即可

#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;=105,m&lt;=5104,mi&lt;=2106n&lt;=10^5,m&lt;=5*10^4,\sum m_i &lt;= 2*10^6

題解: 顯然答案具有單調性 每次二分,用拓撲序判斷 最後輸出的時候用單調佇列即可

#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

題目大意: 給定nn個二元組(v,w)(v,w)和一個正整數WW 要求調出若干個二元組,在滿足wi&gt;=W\sum w_i &gt;= W的條件下最大化viwi\frac{\sum v_i}{\sum w_i} 1&lt;=n&lt;=250,1&lt;=W&lt;=103,1&lt;=wi&lt;=106,1&lt;=vi&lt;=1031&lt;=n&lt;=250,1&lt;=W&lt;=10^3,1&lt;=w_i&lt;=10^6,1&lt;=v_i&lt;=10^3 如果答案為實數x,要求輸出1000x\lfloor 1000x \rfloor

題解: 0/1分數規劃模板題 用實數二分可能會有精度問題,當然卡的好還是能過去 題解給的做法是我們在checkcheck的時候由於輸出的要求,我們可以都乘上10001000的係數,然後用0/10/1揹包直接做

太水了沒寫程式碼