Pro
Sco
预计得分: 100 + 100 + 100 = 300 100 + 100 + 100 = 300 100+100+100=300
实际得分: 100 + 100 + 0 = 200 100 + 100 + 0 = 200 100+100+0=200
被第三题给虐了,打了一份不是正解却自以为正解的代码……
Sol
三角形
测完之后才明白我原来用了一个比较麻烦的解法,不过还好AC了……
因为没有三线共点,所以我们可以得到:任何三条相交的直线都可以构成一个三角形
如果全部都不平行,可以构成 C n 3 C_{n}^{3} Cn3个三角形
任意两两平行都不能构成三角形,任意三条平行线也不能构成三角形
于是答案即为: C n 3 − ∑ i = 1 m ( ( n − c n t [ i ] ) × C c n t [ i ] 2 + C c n t [ i ] 3 ) C_{n}^{3}-\sum_{i=1}^m((n-cnt[i])×C_{cnt[i]}^{2} + C_{cnt[i]}^{3}) Cn3−∑i=1m((n−cnt[i])×Ccnt[i]2+Ccnt[i]3)
其中, m m m为平行线的组数, c n t [ i ] 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)~~的一个 L I S LIS LIS……
(手动模拟一下就可以发现是一个最大上升子序列,我直接打了 n l o g n nlogn nlogn的。
#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
后来才发现之前的想法就有错误。
直接按照 g g g从小到大排序,维护一个最小生成树的边集。
数据规模不大,每一次都克鲁斯卡尔判断是否联通,时间复杂度 O ( n m ) 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;
}