老司机就是老司机……这题真TM好
丢你连接
T1
题目大意
对于每一行,可以选择若干个连续的数,要求平均值最小
NOIP不考double!!!!
最大化平均值?
二分!
单调性?
唔……
不知道!
好吧我们继续……
设答案为x
则有
∑ni=1sum[i][h[i]]∑ni=1[h[i]]≥x
,其中,sum[i][j] = 第i行前j个元素的和,h[i]表示第i行选了多少个元素
//注意,现在h[i]
是未知的
两边同乘
∑ni=1[h[i]]
得
∑ni=1sum[i][h[i]]≥x∗∑ni=1h[i]
即
∑ni=1sum[i][h[i]]≥∑ni=1(h[i]∗x)
移项,得
现在我们将问题转化成了求子集使得上式成立
我们发现如果 上式成立则说明答案x可行
对于每一个 i 我们贪心的选择最大的
恩恩
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int MAXN = 100000 + 5;
int n,m;
vector <double> summer[MAXN];
bool can(double x)
{
double sum = 0;
for(int i = 1;i <= n;i ++)
{
double tmp = -2147483647;
for(int j = 0;j < m;j ++)
tmp = max(summer[i][j] - x * (j + 1),tmp);
sum += tmp;
}
return sum >= 0;
}
double diver(double l,double r)
{
double ans = 0;
for(int i = 1;i <= 100;i ++)
{
double mid = (l + r) / 2;
if(can(mid))
ans = l = mid;
else
r = mid;
}
return ans;
}
double x;
int main()
{
scanf("%d %d",&n,&m);
double r = 0;
for(int i = 1;i <= n;i ++)
{
double s = 0;
for(int j = 1;j <= m;j ++)
{
scanf("%lf",&x);
summer[i].push_back(s + x);
s += x;
r = max(r,x);
}
}
printf("%.4lf",diver(0,r));
return 0;
}
T2
求一个子集,这个子集中的任意两种数字连成的边都有相交
将读入的两个序列分别叫做
up[]和down[]
两条线端有交叉的充要条件是
up[i]≤up[j]∧down[i]≥down[j]
由于线段只会在标号相同的两点之间出现,因此
down[i]≠down[j]
恩恩
我们定义map[i]
表示i在上面的序列(up[])中出 现的位置,并且
up[i] = map[up[i]],down[i] = map[down[i]]
那么上面的序列就变成了
1,2,3,4,5...n
下面的序列就变成了….谁知道呢
因为我们只需要求有多少条线段相交,那么我们改变编码方式不会导致WA
但是这样做之后,第一个条件(
up[i]≤up[j]
)就已经满足了
那么我只需要保证
down[i]≥down[j]
怎么做?
保证i递增并且num[i]递减……
这是什么?
最长下降子序列!
恩恩
求吧
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 100000 + 5;
int n;
int g[MAXN];
int where(int x)
{
return lower_bound(g + 1,g + n + 1,x) - g;
}
int num[MAXN];
int dpdpd()
{
memset(g,0x3f,sizeof(g));
int len = 0;
for(int i = 1;i <= n;i ++)
{
int k = where(num[i]);
g[k] = min(g[k],num[i]);
len = max(len,k);
}
return len;
}
int map[MAXN];
int x;
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
{
scanf("%d",&x);
map[x] = i;
}
for(int i = 1;i <= n;i ++)
{
scanf("%d",&x);
num[i] = -map[x];
}
printf("%d\n",dpdpd());
return 0;
}
T3
题目大意
做题的时候没有发现对于第一种操作,人进入的顺序是一定的,是修改后的dfs序
QAQ
用了巧妙的存图方式
好题
第一问可以将节点以及dfs序丢到堆里
第二问可以倍增lca
不说太多了……放到错题本里
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int MAXN = 100000 + 5;
const int MAX_LOG = 30 + 5;
const int LOG = 20;
vector <int> G[MAXN];
int dfn[MAXN],dfs_clock;
int fa[MAXN][MAX_LOG];
int n,m;
void build(int f,int t)
{
G[f].push_back(t);
return;
}
void dfs(int x,int f)
{
fa[x][0] = f;
for(int i = 0;i < G[x].size();i ++)
if(G[x][i] != f)
dfs(G[x][i],x);
dfn[x] = ++ dfs_clock;
return;
}
bool inque[MAXN];
void make_fa()
{
for(int j = 1;j <= LOG;j ++)
for(int i = 1;i <= n;i ++)
fa[i][j] = fa[fa[i][j - 1]][j - 1];
return;
}
int ask(int &x)
{
int ans = 0;
for(int j = LOG;j >= 0;j --)
if(!inque[fa[x][j]])
x = fa[x][j],ans += 1 << j;
return ans;
}
struct dot
{
int u;
bool operator < (const dot &b)const
{
return dfn[u] > dfn[b.u];
}
};
int f,t;
priority_queue <dot> q;
int Q,x;
int main()
{
scanf("%d %d",&n,&m);
for(int i = 1;i < n;i ++)
{
scanf("%d %d",&f,&t);
build(f,t);
build(t,f);
}
for(int i = 1;i <= n;i ++)
sort(G[i].begin(),G[i].end());
dfs(1,0);
make_fa();
for(int i = 1;i <= n;i ++)
q.push((dot){i}),inque[i] = true;
inque[0] = true;
while(m --)
{
scanf("%d %d",&Q,&x);
int ans;
switch(Q)
{
case 1:
for(int i = 1;i <= x;i ++)
{
ans = q.top().u;
q.pop();
inque[ans] = false;
}
break;
case 2:
ans = ask(x);
inque[x] = true;
q.push((dot){x});
break;
}
printf("%d\n",ans);
}
return 0;
}