noip 膜你赛 考试总结
2017-07-11 13:23
274 查看
其实这些题除了第三题外都是可做的啊……
第一题int被乘爆,而且少讨论了一种情况
第二题摸到了正解的边,但是想来想去不敢写,最后竟然连部分分都没有
第三题完全懵比
第一题就是一个简单的贪心,非常简单,就是细节很多
直接上代码了:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
const int MAXN=105;
typedef long long LL;
using namespace std;
struct node{LL d,m;}zd[MAXN];
LL p[MAXN];
bool cmp(node a,node b){
return a.d<b.d;
}
inline void Read(int &Ret){
char ch;bool flag=0;
for(;ch=getchar(),ch<'0'||ch>'9';)if(ch=='-') flag=1;
for(Ret=ch-'0';ch=getchar(),'0'<=ch&&ch<='9';Ret=Ret*10+ch-'0');
flag&&(Ret=-Ret);
}
LL N,T;
int main()
{
scanf("%I64d",&N); LL num=0;
for(int i=1;i<=N;i++)
{
scanf("%I64d%I64d",&zd[i].m,&zd[i].d);
num+=zd[i].m;//处理出总共要搬多少砖
}
sort(zd+1,zd+N+1,cmp);//按价值排序
scanf("%I64d",&T); LL ans=0;
LL F=1,cnt=0; bool flag=0;
for(int i=1;i<=T;i++)
{
scanf("%I64d",&p[i]);
if(p[i]>num&&!flag) p[i]=num,flag=1;
//如果时间分割点已经大于num,后面那一段已经无意义了
if(p[i]>num) continue;
LL up=p[i]-p[i-1],t;//up是这段时间能搬多少砖,t保存能够搬多少第cnt种的砖
for(LL j=0;j<=up;)
{
if(j+zd[++cnt].m<=up)//如果第cnt种的砖能被全部搬完
{
t=zd[cnt].m; j+=zd[cnt].m;
ans+=t*F*zd[cnt].d;
zd[cnt].m=0;
}
else
{
t=up-j; j=up+1;
zd[cnt].m-=t;
ans+=t*F*zd[cnt--].d;
}
}
F++;
}
if(cnt!=N)
{
for(int i=cnt;i<=N;i++)
ans+=zd[i].d*zd[i].m*F;
}
printf("%I64d",ans);
}
第二题 这道题有一个规律,并不难发现
我们只需要将每条边两端的点的数量处理出来
取最小值乘上边的权值,相加就是答案了
程序如下(中间有段神奇的东西可以使代码在windows的环境下不爆栈)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
const int MAXN=100005;
typedef long long LL;
using namespace std;
struct node{int e,next;LL v;}h[MAXN*2];
int x,y,n,cnt=1,fir[MAXN]; LL z;
int num[MAXN],l[MAXN],r[MAXN]; bool vis[MAXN];
inline void addedge(int t1,int t2,LL v){
h[++cnt].e=t2;
h[cnt].next=fir[t1];
h[cnt].v=v;
fir[t1]=cnt;
}
int dfs(int s){
vis[s]=1; int sum=0;
for(int i=fir[s];i;i=h[i].next)
if(!vis[h[i].e]) sum+=dfs(h[i].e)+1;
return num[s]=sum;
}
int main()
{
/*
int size=16<<20;
char *p=(char*)malloc(size)+size;
__asm__("movl %0, %%esp\n" :: "r"(p));
*/
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d%I64d",&x,&y,&z);
addedge(x,y,z); addedge(y,x,z);
}
dfs(1); LL ans=0;
for(int i=2;i<=cnt;i+=2)
{
int s=h[i].e,e=h[i^1].e;
if(num[s]<num[e]) swap(s,e);
LL w=min(num[e]+1,num[1]-num[e]);
ans+=w*h[i].v;
}
printf("%I64d",ans);
}
第三题众多大佬都说简单,但我楞是连部分分都没有
这道题我觉得对我很有好处,首先是标程中有个O(n)求逆元的,好厉害
然后是一些很用的思路
首先是那个O(n) 求逆元的版
LL inv(int n){
if(n==1) return 1;
else return (mod-mod/n*inv(mod%n)%mod)%mod;
}
给出证明:
转载于:http://blog.miskcoo.com/2014/09/linear-find-all-invert
然后就是思路:
这道题呢,让我们先来考虑n==1的情况,对于这种情况,其实可以完全等价于
一个折线的问题,算了,其实这些题解上都有,不说了,直接重点
当时我唯一没搞懂的地方就是为什么可以直接将假设n==1时的ans给n次方求出答案
其实这是不难理解的,对于子集,它是具有独立性的
怎么说呢,就是假设已经得到一个合法的情况
0 0
0 x
与另一个合法的情况
0 0
0 y
那么我们将这两个合法的方案叠加,可以得到:
0 0
0 (x,y)
仍然是合法的
于是根据乘法原理,那个n次方就不难理解了
第一题int被乘爆,而且少讨论了一种情况
第二题摸到了正解的边,但是想来想去不敢写,最后竟然连部分分都没有
第三题完全懵比
第一题就是一个简单的贪心,非常简单,就是细节很多
直接上代码了:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
const int MAXN=105;
typedef long long LL;
using namespace std;
struct node{LL d,m;}zd[MAXN];
LL p[MAXN];
bool cmp(node a,node b){
return a.d<b.d;
}
inline void Read(int &Ret){
char ch;bool flag=0;
for(;ch=getchar(),ch<'0'||ch>'9';)if(ch=='-') flag=1;
for(Ret=ch-'0';ch=getchar(),'0'<=ch&&ch<='9';Ret=Ret*10+ch-'0');
flag&&(Ret=-Ret);
}
LL N,T;
int main()
{
scanf("%I64d",&N); LL num=0;
for(int i=1;i<=N;i++)
{
scanf("%I64d%I64d",&zd[i].m,&zd[i].d);
num+=zd[i].m;//处理出总共要搬多少砖
}
sort(zd+1,zd+N+1,cmp);//按价值排序
scanf("%I64d",&T); LL ans=0;
LL F=1,cnt=0; bool flag=0;
for(int i=1;i<=T;i++)
{
scanf("%I64d",&p[i]);
if(p[i]>num&&!flag) p[i]=num,flag=1;
//如果时间分割点已经大于num,后面那一段已经无意义了
if(p[i]>num) continue;
LL up=p[i]-p[i-1],t;//up是这段时间能搬多少砖,t保存能够搬多少第cnt种的砖
for(LL j=0;j<=up;)
{
if(j+zd[++cnt].m<=up)//如果第cnt种的砖能被全部搬完
{
t=zd[cnt].m; j+=zd[cnt].m;
ans+=t*F*zd[cnt].d;
zd[cnt].m=0;
}
else
{
t=up-j; j=up+1;
zd[cnt].m-=t;
ans+=t*F*zd[cnt--].d;
}
}
F++;
}
if(cnt!=N)
{
for(int i=cnt;i<=N;i++)
ans+=zd[i].d*zd[i].m*F;
}
printf("%I64d",ans);
}
第二题 这道题有一个规律,并不难发现
我们只需要将每条边两端的点的数量处理出来
取最小值乘上边的权值,相加就是答案了
程序如下(中间有段神奇的东西可以使代码在windows的环境下不爆栈)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
const int MAXN=100005;
typedef long long LL;
using namespace std;
struct node{int e,next;LL v;}h[MAXN*2];
int x,y,n,cnt=1,fir[MAXN]; LL z;
int num[MAXN],l[MAXN],r[MAXN]; bool vis[MAXN];
inline void addedge(int t1,int t2,LL v){
h[++cnt].e=t2;
h[cnt].next=fir[t1];
h[cnt].v=v;
fir[t1]=cnt;
}
int dfs(int s){
vis[s]=1; int sum=0;
for(int i=fir[s];i;i=h[i].next)
if(!vis[h[i].e]) sum+=dfs(h[i].e)+1;
return num[s]=sum;
}
int main()
{
/*
int size=16<<20;
char *p=(char*)malloc(size)+size;
__asm__("movl %0, %%esp\n" :: "r"(p));
*/
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d%I64d",&x,&y,&z);
addedge(x,y,z); addedge(y,x,z);
}
dfs(1); LL ans=0;
for(int i=2;i<=cnt;i+=2)
{
int s=h[i].e,e=h[i^1].e;
if(num[s]<num[e]) swap(s,e);
LL w=min(num[e]+1,num[1]-num[e]);
ans+=w*h[i].v;
}
printf("%I64d",ans);
}
第三题众多大佬都说简单,但我楞是连部分分都没有
这道题我觉得对我很有好处,首先是标程中有个O(n)求逆元的,好厉害
然后是一些很用的思路
首先是那个O(n) 求逆元的版
LL inv(int n){
if(n==1) return 1;
else return (mod-mod/n*inv(mod%n)%mod)%mod;
}
给出证明:
转载于:http://blog.miskcoo.com/2014/09/linear-find-all-invert
然后就是思路:
这道题呢,让我们先来考虑n==1的情况,对于这种情况,其实可以完全等价于
一个折线的问题,算了,其实这些题解上都有,不说了,直接重点
当时我唯一没搞懂的地方就是为什么可以直接将假设n==1时的ans给n次方求出答案
其实这是不难理解的,对于子集,它是具有独立性的
怎么说呢,就是假设已经得到一个合法的情况
0 0
0 x
与另一个合法的情况
0 0
0 y
那么我们将这两个合法的方案叠加,可以得到:
0 0
0 (x,y)
仍然是合法的
于是根据乘法原理,那个n次方就不难理解了
相关文章推荐
- 2015.10.31[NOIP模拟赛]考试总结
- 2017第一周竞赛考试总结(NOIP2010)
- 2016.10.29 清北学堂NOIP冲刺班Day1 AM 考试总结
- NOIP2010原题考试总结
- noip2014 考试总结
- NOIP2010原题考试总结
- [5-14,5-15]NOIP模拟考试总结
- 本次考试总结(2017.8.15上午考试)(noip2016真题)
- NOIP2011模拟考试总结
- 【2016.1.9】【日常考试】NOIP2008提高组 深度解析&总结
- NOIP2009模拟考试总结
- NOIP 2011 day1 真题 考试总结
- NOIP模拟赛2017.9.11 考试心得+总结
- NOIP 7.7TG模拟总结
- 2017年3月23日考试总结
- NOIP总结
- 20151017考试总结
- 期末考试监考总结
- NOIP提高组A组7.8模拟总结
- CCNA考试试验总结