头更大
这个9月完就要去集训搞NOIP了。。。
9月30天也就3次测试。。。为防万一我还是每次测试玩都写个总结。。
第三题强势暴力,第二题浪了。
第一题。。。。。。
mmp读入优化有毒!!!
mmp读入优化有毒!!!
mmp读入优化有毒!!!
跳高
题目背景
SOURCE:NOIP2015-HN-CJZX
题目描述
r64 喜欢跳高。但是他的技术并不好,所以他想好好练习一下。
练习场上有一个个高度不一定一样的平台,如图所示。最底下的是地板,高度为 0 。有 n 个平台,第 i 个平台的高度为 hi (hi≥0)。作为一名跳高爱好者,r64 希望跳到尽可能高的平台。
但是 r64 技术不好,他的最大跳跃高度是 Δh 。也就是说,如果 r64 当前高度为 h1 ,某个平台的高度为 h2 (h2≥h1 ),那么 r64 能跳到这个平台,当且仅当 h2≤h1+Δh 。之后,r64 的高度变成 h2 。
除此之外,r64 规定他自己不能多次跳上同一个平台。
r64 想知道,他最多能跳多高。并且,在跳得尽量高的前提下,他最多经过多少个平台,以及最少经过多少个平台。
输入格式
输入第一行有两个正整数 n 和 Δh ,分别表示平台个数和 r64 的最大跳跃高度。
接下来一行有 n 个非负整数 h1,h2,…,hn,表示每个平台的高度。
输出格式
输出只包括一行三个整数 maxh,maxjump,minjump,分别表示 r64 能跳的最大高度、他跳到高度 maxh 所经过的平台数量的最大值与最小值。
样例数据 1
输入 [复制]
5 2
1 2 3 5 3
输出
5 5 3
样例数据 2
输入 [复制]
9 1
1 2 3 4 6 7 8 9 10
输出
4 4 4
备注
【样例1说明】
经过平台数量为 5 的一种跳法是:0→1→2→3→3→5。
经过平台数量为 3 的一种跳法是:0→2→3→5。当然,0→1→3→5也是允许的。
【数据说明】
一共 20 个测试点,每个测试点的特征如下表所示。
。。。考试时一群人死在没有加读入优化上。。。但更不能理解的是曾老电脑的cin比scanf快了好几倍?????
用scar_lyw的话来说,鬼畜了。
MY/STD.CPP
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<string>
#include<cstring>
#include<ctime>
using namespace std;
int n,m;
int a[1000500],f[1000500];
int maxh=0,maxj=0,minj=0;
int main()
{
// freopen("jump.in","r",stdin);
// freopen("jump.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(NULL);
cin >> n >> m;
for(int i=1;i<=n;i++)
cin >> a[i];
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
{
if(a[i]-a[i-1]>m)break;
else{
maxh = a[i];
maxj += 1;
if(a[i]!=a[i-1])
f[i] = a[i];
}
}
memset(a,127,sizeof(a));
a[0] = 0;
int head=0,tail=0;
while(f[tail]!=maxh)
{
tail++;
while(f[head]+m<f[tail])head++;
a[tail]=min(a[tail],a[head]+1);
}
minj = 999999999;
while(f[head]+m<f[tail])head++;
minj = min(minj,a[head]+1);
if(maxh==0)minj=0;
cout<<maxh<<" "<<maxj<<" "<<minj<<endl;
}
证明
题目背景
SOURCE:NOIP2015-HN-CJZX
题目描述
H 教授是一位德高望重的教授,也是计算机科学界的权威。他对问题总有独特而深刻的见解,治学严谨,是学术界的带头人。
在一次科学家大会上,H 教授在黑板上写下了 n 个式子 x1,x2,…,xn ,并向参加会议的所有科学家证明了:如果 x1=x2=…=xn,那么可以证明 P=NP 。可是,毕竟人无完人,H 教授对其中的任意两个式子是否相等都说不清。他把这个问题抛给了全世界的科学家们。
令人激动的是,没过多久,H 教授就收到了数学家们发来的 m 封 email ,第 i 封 email 写到,发信人已经证明了 ∀li≤a< ri,xa=xa+1,即 xli,x(li)+1,…,xri 两两相等。但是,这些证明是有版权的,如果 H 教授需要使用这些证明,那么需要向提供证明的人支付 ci 元稿费。
H 教授希望通过这些信息证明出 P=NP 。但是,H 教授最近手头拮据,所以希望支付最小的费用。
输入格式
输入的第一行是两个整数 n,m。
接下来 m 行,每行三个整数 li,ri,ci (1≤li≤ri≤n),代表第 i 位数学家的证明及其稿费。
输出格式
输出只包含一个整数,表示 H 教授至少要支付多少元稿费,才能证明出 P=NP。如果根据现有条件无法证明 P=NP ,请输出-1。
样例数据 1
输入 [复制]
9 3
1 3 101010
4 6 98889
7 9 76543
输出
-1
样例数据 2
输入 [复制]
9 7
1 5 3
3 6 8
5 8 4
4 7 6
2 3 7
7 9 2
6 7 5
输出
9
备注
【样例1说明】
就算把所有的数学家都叫上,仍然证明不了 x3=x4 和 x6=x7。
【样例2说明】
第一位数学家可以证明 x1=x2=x3=x4=x5。
第三位数学家可以证明 x5=x6=x7=x8。
第六位数学家可以证明 x7=x8=x9。
这三位数学家是足够的,并且只需 9 元稿费。可以证明没有更优的方案。
【数据说明】
所有测试点的数据范围如下表所示。
一开始觉得标答无望就乱打了个有向图套Dijkstra(边多)后来发现还差点打成标答。。。但是建边的时候瓜了。
额我具体怎么挂的自己看吧。
MY.CPP
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<string>
#include<cstring>
#include<ctime>
#include<queue>
using namespace std;
const int kkk=100055;
int n,m;
struct nde{
int l,r,c;
}s[kkk];
bool comp(const nde &a,const nde &b)
{if(a.l==b.l)return a.r<b.r;return a.l<b.l;}
struct node{
int u,v,val,next;
}side[6*kkk];
int cnt=0,first[kkk];
void addedge(int u,int v,int val)
{
cnt += 1;
side[cnt].u = u;
side[cnt].v = v;
side[cnt].val = val;
side[cnt].next = first[u];
first[u] = cnt;
}
bool visit[kkk];
long long dis[kkk];
long long dij(int s,int t)
{
memset(dis,126,sizeof(dis));
priority_queue< pair<long long,int> >que;
que.push(make_pair(0,s));
dis[s] = 0;
long long gud = dis[t];
while(!que.empty())
{
pair<int,int> k = que.top();
que.pop();
visit[k.second] =true;
for(int j=first[k.second];j;j=side[j].next)
{
int v = side[j].v;
if(!visit[v] && dis[v]>dis[k.second]+side[j].val)
{
dis[v] = dis[k.second]+(long long)side[j].val;
que.push(make_pair(-dis[v],v));
}
}
}
if(dis[t]==gud)return -1;
return dis[t];
}
int main()
{
// freopen("proof.in","r",stdin);
// freopen("proof.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>s[i].l>>s[i].r>>s[i].c;
sort(s+1,s+m+1,comp);
int head=1,tail=1;
addedge(s[tail].l,s[tail].r,s[tail].c);
while(tail!=m)
{
tail++; addedge(s[tail].l,s[tail].r,s[tail].c);
while(s[head].r<s[tail].l)head++;
for(int i=head;i<tail;i++)
addedge(s[i].l,s[tail].l,s[i].c);
}
cout << dij(1,n) << endl;
}
哦对了标答还要记得套个离散化还有把数组开到1e6不然会卡四个点。
STD.CPP
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<string>
#include<cstring>
#include<ctime>
#include<queue>
using namespace std;
const int kkk=1000055;
int n,m;
struct nde{
int l,r;
long long c;
}s[kkk];
bool comp(const nde &a,const nde &b)
{if(a.l==b.l)return a.r<b.r;return a.l<b.l;}
struct node{
int u,v,val,next;
}side[6*kkk];
int cnt=0,first[kkk];
void addedge(int u,int v,int val)
{
cnt += 1;
side[cnt].u = u;
side[cnt].v = v;
side[cnt].val = val;
side[cnt].next = first[u];
first[u] = cnt;
}
bool visit[kkk];
long long dis[kkk];
long long dij(int s,int t)
{
memset(dis,126,sizeof(dis));
priority_queue< pair<long long,int> >que;
que.push(make_pair(0,s));
dis[s] = 0;
long long gud = dis[t];
while(!que.empty())
{
pair<int,int> k = que.top();
que.pop();
visit[k.second] =true;
for(int j=first[k.second];j;j=side[j].next)
{
int v = side[j].v;
if(!visit[v] && dis[v]>dis[k.second]+side[j].val)
{
dis[v] = dis[k.second]+(long long)side[j].val;
que.push(make_pair(-dis[v],v));
}
}
}
if(dis[t]==gud)return -1;
return dis[t];
}
int num[2*kkk],tot=0;
int main()
{
// freopen("proof.in","r",stdin);
// freopen("proof.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>s[i].l>>s[i].r>>s[i].c;
num[++tot]=s[i].l;num[++tot]=s[i].r;
}
num[++tot]=1; num[++tot]=n;
sort(num+1,num+tot+1);
n = unique(num+1,num+tot+1)-num-1;
tot = n;
for(int i=1;i<=m;i++)
{
s[i].l=lower_bound(num+1,num+tot+1,s[i].l)-num;
s[i].r=lower_bound(num+1,num+tot+1,s[i].r)-num;
addedge(s[i].l,s[i].r,s[i].c);
}
for(int i=2;i<=n;i++)
addedge(i,i-1,0);
cout << dij(1,n) << endl;
}
树
题目背景
SOURCE:NOIP2015-HN-CJZX
题目描述
树(tree)是指由 n 个点,n-1 条边构成的连通无向图。如果有一棵树,它的每一条边 (u,v) 都有一个权值 lu,v ,我们把这样的树称作带权树(weighted tree)。
我们知道对于树上的任意两个点,他们之间的路径是唯一的。对于两个点 u,v 来说,我们可以算出 u 与 v 之间的路径上所有边权之和,将其称作 u 与 v 之间路径的长度,记作 d(u,v)。
你的任务是计算:
输入格式
输入第一行为一个正整数 n 。
接下来 n-1 行,每行三个非负整数 x,y,w,表示点 x 与点 y 之间有一条权值为 w 的一条边。
保证输入的图是一棵树。
输出格式
输出仅包含一行一个数,即答案。因为结果可能很大,请将答案模 100 000 007 输出。
样例数据 1
输入 [复制]
4
1 2 4
1 3 4
1 4 4
输出
72
样例数据 2
输入 [复制]
10
1 2 1320321
2 3 4687651
3 4 1321357
4 5 6651332
5 6 5746513
6 7 5674687
7 8 7451216
8 9 7789965
9 10 8765134
输出
28244404
备注
【样例1说明】
d(1,2)+d(1,3)+d(1,4)=4+4+4=12
d(2,1)+d(2,3)+d(2,4)=4+8+8=20
d(3,1)+d(3,2)+d(3,4)=4+8+8=20
d(4,1)+d(4,2)+d(4,3)=4+8+8=20
所以答案为:12+20+20+20=72。
【数据范围】
共 20 个测试点,其数据特征如下表所示。
对所有数据,有 1≤n≤3×105,0≤w<100 000 007。数据有一定梯度。
只能说自己考试时脑抽的吓人。正解很显然枚举每一条边,把边左右两边的点的个数和边权值乘起来就行了。
然而当我想到这一步的时候。。。我。竟。然。不。知。道。怎。么。求。一。条。边。两。边。的。点。的。个。数。了。。。。。然后打了个LCA的超级大暴搜。。。
后来看了一眼题解才发现一遍DFS完事。。。。。。
MY.CPP
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
using namespace std;
int n,m,x,y,u,v,val;
struct node{
int u,v,val,next;
}side[600010];
int cnt=0,first[300010];
void addedge(int u,int v,int val)
{
cnt += 1;
side[cnt].u = u;
side[cnt].v = v;
side[cnt].val = val;
side[cnt].next = first[u];
first[u] = cnt;
}
bool visit[300010];
int dep[300010];
long long dis[300010];
int f[300010][20];
void dfs(int root)
{
visit[root] = true;
for(int i=first[root];i;i=side[i].next)
{
int u = side[i].u; int v = side[i].v;
if(!visit[v])
{
f[v][0] = u;
dep[v] = dep[u] + 1;
dis[v] = dis[u] + (long long)side[i].val;
dfs(v);
}
}
}
#define maxx 300005
void initi(int root)
{
memset(dep,0,sizeof(dep));
dis[root] = 0;
dep[root] = 0;
dfs(root);
for(int i=1;i<=18;i++)
for(int j=1;j<=maxx;j++)
if(f[j][i-1])
f[j][i] = f[f[j][i-1]][i-1];
}
int lca(int x,int y)
{
if(dep[x]<dep[y]){int k=x;x=y;y=k;}
int foot = dep[x] - dep[y];
for(int i=18;i>=0;i--)
if(foot>=(1<<i)) foot -= (1<<i),x=f[x][i];
if(x==y) return x;
for(int i=18;i>=0;i--)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
int main()
{
// freopen("tree.in","r",stdin);
// freopen("tree.out","w",stdout);
cin >> n ;
for(int i=1;i<n;i++)
{
cin >> u >> v >> val;
addedge(u,v,val);
addedge(v,u,val);
}
initi(1);
long long ans = 0;
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++)
{
ans += 2*(dis[i]+dis[j]-2*dis[lca(i,j)]);
ans %= 100000007;
}
cout << ans << endl;
}
STD.CPP
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<string>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<set>
#include<map>
using namespace std;
const int kkk=300005;
const int nod=100000007;
long long n,ans,son[kkk],fa[kkk];
long long cnt,first[kkk];
struct node
{
int u,v,next;
long long val;
}side[2*kkk];
inline long long read()
{
long long i=0; char ch;
ch=getchar();
while(ch>'9'||ch<'0')ch=getchar();
while(ch<='9'&&ch>='0')
{
i=(i<<3)+(i<<1)+ch-'0';
ch=getchar();
}
return i;
}
inline void add(int x,int y,long long val)
{
side[++cnt].v=y;
side[cnt].u=x;
side[cnt].next=first[x];
side[cnt].val=val;
first[x]=cnt;
}
inline void dfs(int root)
{
for(int i=first[root];i;i=side[i].next)
if(side[i].v!=fa[root])
{
fa[side[i].v]=root;
dfs(side[i].v);
son[root]+=son[side[i].v];
}
}
int main()
{
n=read();
for(int i=2;i<=n;++i)
{
int x,y; long long val;
x=read();y=read();val=read();
add(x,y,val); add(y,x,val);
son[i]=1;
}
son[1]=1; dfs(1);
for(int i=1;i<=cnt;++i)
{
int o=min(son[side[i].v],son[side[i].u]);
long long j=(long long)o*(long long)(n-o);
j%=nod; j*=side[i].val;
j%=nod; ans+=j;
ans%=nod;
}
cout<<ans<<endl;
return 0;
}
感想
暴力出奇迹
然后平时多做些有关树和Dp的题
(话说Dp我还没搞。。。orz)