1. 程式人生 > >Noip模擬題解題報告

Noip模擬題解題報告

Pro

題目連結

Sco

預計得分:100+100+100=300100 + 100 + 100 = 300

實際得分:100+100+0=200100 + 100 + 0 = 200

被第三題給虐了,打了一份不是正解卻自以為正解的程式碼……

Sol

三角形

測完之後才明白我原來用了一個比較麻煩的解法,不過還好AC了……

因為沒有三線共點,所以我們可以得到:任何三條相交的直線都可以構成一個三角形

如果全部都不平行,可以構成 Cn3C_{n}^{3}個三角形

任意兩兩平行都不能構成三角形,任意三條平行線也不能構成三角形

於是答案即為:Cn3i=1m((ncnt[i])×Ccnt[i]2+Ccnt[i]3)C_{n}^{3}-\sum_{i=1}^m((n-cnt[i])×C_{cnt[i]}^{2} + C_{cnt[i]}^{3})

其中,mm為平行線的組數,cnt[i]cnt[i]為平行線的條數。

組合數可以預處理出來,平行線可以用斜率來做。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

const int L = 300005;
double k[L] , temp;
int n , cnt[L] , opt;
long long C2[L] , C3[L] , ans;

void init() {
	sort(k+1 , k+n+1);
	temp = k[1];
	opt = 1;
	cnt[opt] = 1;
	for(int i=2; i<=n; i++) {
		if(k[i] == temp)
			cnt[opt]++;
		else {
			opt++;
			cnt[opt] = 1;
			temp = k[i];
		}
	}
	for(int i=1; i<=n; i++) {
		C2[i] = (long long)((long long)(i-1)*i)/2;
		C3[i] = (long long)((long long)(i-2)*(i-1)*i)/6;		
	}
}

int main() {
	freopen("trokuti.in","r",stdin);
	freopen("trokuti.out","w",stdout);
	scanf("%d",&n);
	for(int i=1; i<=n; i++) {
		int a , b , c;
		scanf("%d%d%d",&a,&b,&c);
		if(b!=0)
			k[i] = -(1.0*a/b);
		else
			k[i] = 1e9;
	}
	init();
	ans = C3[n];
	for(int i=1; i<=opt; i++) {
		long long data = (n-cnt[i])*C2[cnt[i]] + C3[cnt[i]];
		ans -= data;
	}
	printf("%lld",ans);
	return 0;
}

列車排程

很裸~~(shui)~~的一個LISLIS……

(手動模擬一下就可以發現是一個最大上升子序列,我直接打了nlognnlogn的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int n , num[100005] , res[100005] , ans;
inline int mymax(int a , int b) { return a>b?a:b; }

int main() {
	freopen("manage.in","r",stdin);
	freopen("manage.out","w",stdout);
	scanf("%d",&n);
	memset(res , 0x3f , sizeof(res));
	for(int i=1; i<=n; i++)
		scanf("%d",&num[i]);
	res[1] = num[1];
	for(int i=2; i<=n; i++) {
		int opt = lower_bound(res+1 , res+n+1 , num[i]) - res;
		res[opt] = num[i];
		ans = mymax(ans , opt);
	}
	printf("%d",ans);
	return 0;
}

保留道路

第一次的保齡程式碼竟然想到了二分!恐怖.jpg

後來才發現之前的想法就有錯誤。

直接按照gg從小到大排序,維護一個最小生成樹的邊集。

資料規模不大,每一次都克魯斯卡爾判斷是否聯通,時間複雜度O(nm)O(nm)

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

struct Road {
	long long from , to , g , s , vis;
};
Road e[50005] , tree[50005] , temp[50005];
long long n , m , WG , WS , cnt , fa[405] , ans = 1e18;
inline long long mymax(long long a , long long b) { return a>b?a:b; }
inline long long mymin(long long a , long long b) { return a<b?a:b; }

bool cmp(Road a , Road b) {
	if(a.g == b.g)
		return a.s < b.s;
	return a.g < b.g;
}

long long find(long long x) {
	if(fa[x] != x)
		return find(fa[x]);
	return fa[x];
}

void klus(long long mg) {
	long long ms = 0 , tot = 0;
	for(long long i=1; i<=cnt; i++) {
		temp[i] = tree[i];
		temp[i].vis = false;
	}
	for(long long i=1; i<=n; i++)
		fa[i] = i;
	for(long long i=1; i<=cnt; i++) {
		long long fx = find(tree[i].from) , fy = find(tree[i].to);
		if(fx != fy) {
			ms = mymax(ms , tree[i].s);
			fa[fx] = fy;
			tot++;
			temp[i].vis = true;
		}
		if(tot == n-1) {
			long long pot = 0;
			for(int j=1; j<=cnt; j++)
				if(temp[j].vis)
					tree[++pot] = temp[j];
			cnt = pot;
			ans = mymin(ans , ms+mg);
			break;
		}
	}
}

int main() {
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
	scanf("%lld%lld%lld%lld",&n,&m,&WG,&WS);
	for(long long i=1; i<=m; i++) {
		scanf("%d%d%lld%lld",&e[i].from,&e[i].to,&e[i].g,&e[i].s);
		e[i].g = (long long)e[i].g * WG;
		e[i].s = (long long)e[i].s * WS;
	}
	sort(e+1 , e+m+1 , cmp);
	for(int i=1; i<=m; i++) {
		if(e[i].g + e[i].s > ans)
			continue;
		long long opt = cnt + 1;
		for(int j=1; j<=cnt; j++)
			if(e[i].s < tree[j].s) {
				opt = j;
				break;
			}
		if(opt == cnt + 1)
			tree[++cnt] = e[i];
		else {
			for(int j=++cnt;j>=opt+1;j--)
				tree[j]=tree[j-1];
			tree[opt]=e[i];
		}
		klus(e[i].g);
	}
	printf("%lld",(ans==1e18)?(-1):ans);
	return 0;
}