您的位置:首页 > 其它

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次方就不难理解了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: