10.26.2017 考试总结与解题报告

来源:互联网 发布:沈阳数据恢复 编辑:程序博客网 时间:2024/05/22 03:05

卷积

(convolution.pas/c/cpp)

【问题描述】

我们先介绍 Dirichlet 卷积
在 算术函数集 上,可以定义一种二元运算,使得取这种运算为乘法,取普通
函数加法为加法,使得 算术函数集 为一个交换环。其中一种这样的运算便是
Dirichlet 卷积。它和一般的卷积有不少相类之处。
对于算术函数f,g,定义其* Dirichlet 卷积*

(fg)(n)=d|nf(d)g(nd)

Dirichlet 卷积有很多美妙的性质:
1. 取* Dirichlet 卷积为运算,积性函数集是算术函数集的子群*
2. 交换律 fg=gf
3. 结合律 (fg)h=f(gh)
4. 分配律 (f+g)h=fg+fh=h(f+g)
5. 存在单位函数ε 使得 fε=εf。 ε(n)的值为 1 若n=1,否则ε(n)=0
6. 对于任意算术函数f,若f(1) 不等于 0,都有唯一的 逆函数 f1,使得
ff1=ε默比乌斯函数 μ的 逆函数 为(一般意义上的) 1,即对于
n1,d|nμ(d)×1=0

这是默比乌斯反转公式的原理。
这些性质使得* Dirichlet 卷积*在数论中有着特别重要的地位。
现在定义一种运算有序序列的 Dirichlet 卷积K 次自乘,假设原序列为f,结
果序列为g,则有:
g=ff...ff
Kf
现在给出f,我们需要知道fDirichlet 卷积 K次自乘

【输入】

输入文件名为 convolution .in,共 2 行。
第一行包含两个正整数N,K
下面一行N个非负整数,第i个数表示fi

【输出】

输出文件名为 convolution .out,共一行,第i个数表示gimod1000000009

【输入输出样例】

convolution.in

5 2
50 56 85 43 60

convolution.out

2500 5600 8500 7436 6000

【数据说明】

对于 30%的数据满足,K=1;

对于100%的数据满足,1N1e4,1K10,0fi1e5

题目大意

要是我能总结看懂题目大意,我这题还用骗分

思路

模拟

这可能是我写过的最短的题解了

代码

#include<bits/stdc++.h>using namespace std;typedef long long ll;#define mod 1000000009#define cl(x) memset(x,0,sizeof(x))#define rg registertemplate <typename _Tp> inline void read(_Tp &x){char c11=getchar();x=0;bool booo=0;    while(c11<'0'||c11>'9'){if(c11=='-')booo=1;c11=getchar();}    while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}    if(booo)x=-x;return ;}const int maxn=100020;ll n,k,f[maxn],d[maxn],now[maxn];void init();void work(){    for(rg int i=1;i<k;++i){        cl(now);        for(rg int j=1;j<=n;++j)        for(rg int l=1;l*j<=n;++l)            now[l*j]=(now[l*j]+d[j]*f[l])%mod;        for(rg int j=1;j<=n;++j)d[j]=now[j];    }    for(rg int i=1;i<=n;++i)printf("%lld ",d[i]);}int main(){    freopen("convolution.in","r",stdin);    freopen("convolution.out","w",stdout);    init();    work();    return 0;}void init(){    read(n);read(k);    for(rg int i=1;i<=n;++i)read(f[i]),d[i]=f[i];}

巡访

(path.pas/c/cpp)

【问题描述】
Chanxer 终于当上了“中华农民联盟”的盟主,他举目四望,决定四处走走,
巡视自己的农土。
“中华农民联盟”的成员有N个村庄,在“村村通”计划中,村庄们被N − 1条
道路联通了起来,Chanxer 计划从某个村庄出发,访问所有的村庄。
可是 Chanxer 出行有一个特殊的要求,那就是必须以农车代步,现在我们知
道哪些村庄配备有农车,也就是说,只有配备有农车的村庄才能够被作为出发点。
Chanxer 有点懒,他想知道访问全部的村庄所要走的路程长度最小是多少。

【输入】

输入文件名为 path.in,共N+1行,第一行包含一个正整数N
接下来N1行,每行包含三个个整数xi , yi , zi ,表示xi 号村庄和yi 号村庄之
间有一条长度为zi 的路。
接下来一行包含N个数,每个数不是 0 就是 1,若第i个数为 1 表示可以选择
该点为起始点,否则不可以选。

【输出】
输出文件名为 path.out,共一行,包含一个非负整数,表示最小的路程长度,
若无法访问全部村庄则输出1

【输入输出样例】

path.in
5
1 2 1
1 3 1
3 4 3
3 5 4
1 1 1 0 0

path.out
12

【数据说明】
6个测试点的数据满足,N10;
100%的数据满足,N1e5,1xi,yiN,1zi1e4

题目概要

给定一棵边有权值的树,求从给定允许的点中选一点开始遍历,求最小路径

思路

6分解法

puts(“-1”);

24分解法

一看便知树型Dp,g[i]f[i]分别表示从i这儿出发遍历子树不回来与回来的最小权值数,做n次Dp

100分做法

感性地想一想,一条边最多走两次:去与来

那么我们考虑答案的补集,就是有哪些边不用走两次,由于要遍历一棵树,必须遍历所有边,所以答案转化为求哪些边只走一遍

由于路线必须是连续的(也就是一条链),所以我们又把题目转化为求树上最长链

为什么是最长链呢?

因为要求路径最小,而全集一定,所以要求补集最大,也就是树上最长链

24分树型Dp解法程序

#include<bits/stdc++.h>using namespace std;#define inf 2000000050#define cl(x) memset(x,0,sizeof(x))#define cl1(x) memset(x,-1,sizeof(x))#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))#define rg registertemplate <typename _Tp> inline void read(_Tp &x){    char c11=getchar();x=0;    while(c11<'0'||c11>'9')c11=getchar();    while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}return ;}const int maxn=100050,maxm=200050;struct node {int v,w,nxt;} a[maxm];int head[maxn],p=0;bool bo[maxn];int f[maxn],g[maxn];int ans=inf;int n;inline void add(int,int,int);void init();void dfs(int x,int las){    f[x]=0;    for(rg int i=head[x];i;i=a[i].nxt)if(a[i].v!=las){        int v=a[i].v,w=a[i].w;        dfs(v,x);        f[x]+=w*2+f[v];    }    g[x]=f[x];    int maxx=0;    for(rg int i=head[x];i;i=a[i].nxt)if(a[i].v!=las)        maxx=max(maxx,a[i].w+f[a[i].v]-g[a[i].v]);    g[x]-=maxx;}void work(){    for(rg int i=1;i<=n;++i)    if(bo[i]){        dfs(i,0);        ans=min(ans,g[i]);    }    if(ans!=inf)        printf("%d\n",ans);    else         printf("-1\n");    return ;}int main(){    freopen("path.in","r",stdin);    freopen("path.out","w",stdout);    init();    work();    return 0;}void init(){    read(n);    int A,B,C;    for(rg int i=1;i<n;++i){        read(A);read(B);read(C);        add(A,B,C);add(B,A,C);    }    for(rg int i=1;i<=n;++i)read(bo[i]);}inline void add(int u,int v,int w){    a[++p].v=v;    a[p].w=w;    a[p].nxt=head[u];    head[u]=p;}

大炮

(noligon.pas/c/cpp)

【问题描述】

“农气大炮”是 Chanxer 毕生精力凝结而成的心血。“当大炮建成时,普天之下,莫非农土!”,现在 Chanxer 还有最后一步,就是为大炮装载农气续航系统,换句话说,就是上电池。Chanxer 现在有N条能量棒,第 i 条的长度为2ki ,且拥有一个不稳定值Wi 。为了避免因为单次高强度攻击而导致大炮瘫痪,Chanxer 觉得采用能源分离装置,他准备了许多能量槽,每个能量槽都是条形的,横截面积恰好容得下一根
能量棒插入,而能量槽的深度也为2t 的形式。现在,Chanxer 需要再能量棒中选择一些插入能量槽中,为了保证续航时间的最大化,Chanxer 要求每个能量槽都必须完全装满,也就是说,插入其中的能量棒的最长度要正好等于能量槽的深度,与此同时,Chanxer 希望被选择的能量棒的总不稳定值最小。

【输入】

输入文件名为 noligon.in,共N+M+2行。
第一行包含一个整数N
接下来 N 行,每行两个整数ki,Wi
接下来一行包含一个整数M
接下来M行,每行两个数t,h,表示深度为2t 的能量槽有h个。

【输出】

输出文件名为 noligon.out,共一行。
输出一个数表示最小的总不稳定值,假如不存在解就输出1

【输入输出样例】

noligon.in51 31 23 52 11 421 12 1noligon.out3

【数据说明】
对于30%的数据满足,1n,m12;
对于100%的数据满足, 1n10000,0ki1,000,0Wi10,000,0t1000,h0,h5000

题目概要

给定n件物品,m个格子(空间是2k的形式),求填满每个格子的最小花费

思路

10分的’-1’骗分就不说了

直接说正解, 发现它的空间很有特色,是2k的形式,我们凭借幼儿园奥数知识可知,

2k+2k=2k+1
那么我们可以将其一步步递推

接下来开始
首先,需知道有没有解:
为了方便描述,记Numi(i>=0)为尺寸为I的盒子个数;Tii>=0为尺寸为I的集装箱个数;
①由于尺寸为0(大小20)的集装箱必须由尺寸为0的盒子来填,所以,如果Num[0]比T[0]小则必定无解,否则能把尺寸为0的集装箱的填满;
②由于尺寸为1(大小21)的集装箱必须由尺寸为0或1的盒子来填,并且只能包含偶数个尺寸为0的组成,而尺寸为0的盒子还剩下Num[0]-T[0]个,这些盒子显然可以两两组合成(Num[0]-T[0])DIV 2个尺寸为1的盒子,故可看作总共有(Num[1]+(Num[0]-T[0])DIV 2)个尺寸为1的盒子,所以Num[1]+ Num[1]+(Num[0]-T[0])DIV 2,Num[0]+0,在按与第一步类似的方法处理即可;
③以此类推;
④若尺寸为0~1000的集装箱都可填满,则必定有解。
然而,增么得到最优解呢?
实际上,只需在求可行解时多加一些运算便可以保证可行解为最优了。
即:1、第k步时,若非无解情况,则选前T[k-1]个价值最小的盒子;
2、第k步时,剩下Num[k-1]-T[k-1]个盒子,则把它从小到大排序后,让第2*I-1与2*I(I>=1)个盒子结合成一个尺寸为k的大盒子。
这样是可以得到最优值。
证明:若存在可行解,而在某一步k中,选的不是前T[k-1]个价值最小的盒子;我们不妨设取了一个价值为b的尺寸为k-1的盒子,且b不是前T[k-1]小的,则必定有一个(或更多)前T[k-1]小的没取,设它的价值为a,显然有b>=a,那我们将原方案中这两个盒子的位子交换一下(若交换前价值为a的这个没有被选,则交换后不选价值为b的这个盒子),就可能得到更优的解(至少不会差),所以,每次都要选价值最小的前几个,而第2步也不难明白:这样组合能使得到的Num[k-1]-T[k-1]DIV 2个盒子的前j(1<=j<=Num[k-1]-T[k-1]DIV 2)小的价值和达到最小,从而使后面的选择也正确。

执行步骤:

1. 读入数据;
2. 分别对不同尺寸的盒子按升序排序,存在value[I]数组中(value[I]中按升序保存所有尺寸为I的盒子的价值);
3. K0,Min0;
4. 若Num[k] < T[k]则转9;
5. 选出value[k]中1到T[k]个盒子,MinMin+value[k,1]+value[k,2] +…value[k,T[k]];
6. 若k<1000,则将剩下的T[k]-Num[k]个盒子,两两组合成(T[k]-Num[k])DIV 2个尺寸为k+1的{第T[k]+1与T[k]+2,T[k]+3与T[k]+4…,组合},在此过程中,把新生成的(T[k]-Num[k])DIV 2归并入value[k+1]中,且Num[k+1]Num[k+1]+ (T[k]-Num[k])DIV 2
7. K:=K+1,若k<=1000转4否则输出Min;
8. 结束
9. 输出无解,转8

代码

#include<bits/stdc++.h>using namespace std;typedef long long ll;#define cl(x) memset(x,0,sizeof(x))#define rg registertemplate <typename _Tp> inline void read(_Tp &x){    char c11=getchar();x=0;    while(c11<'0'||c11>'9')c11=getchar();while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}    return ;}const int maxn=10050;int m[maxn];int n,ans=0;priority_queue <ll,vector<ll>,greater<ll> > q[1500];int maxl=0,M;void init();void work(){    for(rg int i=0;i<=maxl;++i){        while(m[i]){            if(q[i].empty()){printf("-1");return ;}            ans+=q[i].top();q[i].pop();            --m[i];        }        int temp;        while(!q[i].empty()){            temp=q[i].top();q[i].pop();            if(q[i].empty())break;            q[i+1].push(temp+q[i].top());            q[i].pop();        }    }    printf("%d\n",ans);}int main(){    freopen("noligon.in","r",stdin);    freopen("noligon.out","w",stdout);    init();    work();    return 0;}void init(){    read(n);    cl(m);    int A,B;    for(rg int i=1;i<=n;++i){        read(A);read(B);        q[A].push(B);    }    read(M);    for(rg int i=1;i<=M;++i){        read(A);read(B);        m[A]+=B;        if(maxl<A)maxl=A;    }}
原创粉丝点击