一个“行走的结题报告”出的题目

来源:互联网 发布:excel数据自动更新 编辑:程序博客网 时间:2024/05/17 03:33

第一题:一到简单题

题目描述
Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会。当然,她会选择最方便的地点来举办这次集会。每个奶牛居住在 N(1<=N<=100,000) 个农场中的一个,这些农场由N-1条道路连接,并且从任意一个农场都能够到达另外一个农场。道路i连接农场A_i和B_i(1 <= A_i <=N; 1 <= B_i <= N),长度为L_i(1 <= L_i <= 1,000)。集会可以在N个农场中的任意一个举行。另外,每个牛棚中居住着C_i(0 <= C_i <= 1,000)只奶牛。在选择集会的地点的时候,Bessie希望最大化方便的程度(也就是最小化不方便程度)。比如选择第X个农场作为集会地点,它的不方便程度是其它牛棚中每只奶牛去参加集会所走的路程之和,(比如,农场i到达农场X的距离是20,那么总路程就是C_i*20)。帮助Bessie找出最方便的地点来举行大集会。
输入
第一行:一个整数N
第二到N+1行:第i+1行有一个整数C_i
第N+2行到2*N行,第i+N+1行为3个整数:A_i,B_i和L_i。
输出
一个数字表示答案
样例
Input Output
5
1
1
0
0
2
1 3 1
2 3 2
3 4 3
4 5 3 15

Output
15

时间 :
1s
数据范围
20%数据n<20
50%数据 n<2000
100%数据n<100,000

具体代码

#include<iostream>#include<cstdio>#include<cmath>#define N 100005#define LL long longusing namespace std;int n,num[N],tot[N],head[N];struct ii{    int to,next,w;    ii (int to=0,int next=0,int w=0):to(to),next(next),w(w){}}Edge[N*2];LL f[N],ans=1e12;bool bo1[N],bo2[N];int dfs1(int x,int distant){    int q=head[x];    if(q==0)return num[x];    while(q){        int tov=Edge[q].to;        if(!bo1[tov]){            distant+=Edge[q].w;            f[1]+=(distant)*num[tov];            bo1[tov]=true;            dfs1(tov,distant);            tot[x]+=tot[tov];            distant-=Edge[q].w;        }        q=Edge[q].next;    }}void dfs2(int x){    int q=head[x];    if(q==0)return;    while(q){        int tov=Edge[q].to;        if(!bo2[tov]){            int len=Edge[q].w;            f[tov]=f[x]+tot[1]*len-tot[tov]*len*2;            bo2[tov]=true;            dfs2(tov);        }        q=Edge[q].next;    }}int main(){    freopen("A.in","r",stdin);    freopen("A.out","w",stdout);    scanf("%d",&n);    for(int i=1;i<=n;i++){        scanf("%d",&num[i]);        tot[i]=num[i];    }    for(int i=1;i<=n-1;i++){        int x,y,z;        scanf("%d%d%d",&x,&y,&z);        Edge[i*2-1]=ii(x,head[y],z);        head[y]=i*2-1;        Edge[i*2]=ii(y,head[x],z);        head[x]=i*2;    }    bo1[1]=true;    bo2[1]=true;    dfs1(1,0);    dfs2(1);    for(int i=1;i<=n;i++)    ans=min(ans,f[i]);    cout<<ans<<endl;    return 0;}

第二题:一道更简单的题

时间
1s
描述
有n个数围成一圈,每次操作后每个数变成和他距离在d以内的数字之和,求k次操作后每个数字模1000000007分别是多少
输入
第一行三个数n, d, k (1 ≤ n ≤ 1000, 0 ≤ d < n / 2 , 1 ≤ k ≤ 100 000 000),
第二行有n个正数,每个的大小都在int范围内
输出
一行n个数,空格隔开,表示结果。
样例
Input Ouput
5 1 1
1 2 10 3 6
9 13 15 19 10

Ouput
9 13 15 19 10

数据范围
10%数据满足n*k<10^6
30%数据满足 n<=100
50%数据满足 n<=500
100%数据满足n<=1000

解题思路
这道题真真666
先看代码
My 代码

#include<iostream>#include<cstdio>#include<cstring>#define N 1005#define M 1000000007#define LL long longusing namespace std;int n,d,k;struct ii{    int ju[N];    ii(){memset(ju,0,sizeof(ju));}};ii operator *(const ii a,const ii b){    ii c;    for(int i=0;i<n;i++)    for(int j=0;j<n;j++){        long long x=(((long long)a.ju[j]*b.ju[(i>=j)?(i-j):(n+i-j)])%M+c.ju[i])%M;        c.ju[i]=x;    }    return c;}ii init;ii Pow(ii a,int b){    if(b==1)return a;    ii temp=Pow(a,b/2);    if(b&1)return a*(temp*temp);    else return temp*temp;}ii tmp;int main(){    freopen("B.in","r",stdin);    freopen("B.out","w",stdout);    cin>>n>>d>>k;    for(int i=0;i<n;i++)    scanf("%d",&init.ju[i]);    for(int i=0;i<d;i++)    tmp.ju[i]=tmp.ju[n-i-1]=1;    tmp.ju[d]=1;    ii ans;    ans=Pow(tmp,k);    ans=init*ans;    for(int i=0;i<n;i++)printf("%d ",ans.ju[i]);    return 0;}

标程

#include <iostream>#include <iomanip>#include <fstream>#include <stdlib.h>#include <time.h>#include<cstring>#include<cstdio>#include<vector>#include<string>#include<algorithm>#include <limits.h>#include<cmath>#include<map>#include<queue>#include<set>using namespace std;long long n,d,k;long long m=1000000007;void mul(long long a[],long long b[]){      int i,j;      long long c[1501];      for(i=0;i<n;++i)for(c[i]=j=0;j<n;++j)c[i]+=a[j]*b[i>=j?(i-j):(n+i-j)]%m;      for(i=0;i<n;b[i]=c[i++]%m);}long long init[1501],tmp[1501];int main(){    freopen("B.in","r",stdin);    freopen("B.out","w",stdout);    long long  i,j;    scanf("%lld%lld%lld",&n,&d,&k);    //k=1;d=1;    for(i=0;i<n;++i)scanf("%lld",&init[i]);    for(tmp[0]=i=1;i<=d;++i)tmp[i]=tmp[n-i]=1;    while(k)    {            if(k&1)mul(tmp,init);            mul(tmp,tmp);            k>>=1;    }    for(i=0;i<n;++i)if(i)printf(" %lld",init[i]);else printf("%lld",init[i]);    printf("\n");    return 0;}

有一点区别,不过核心都是一样的,“行走的结题报告”6666666
我的程序是用的快速幂矩阵乘法
如样例
init:1 2 10 3 6
tmp: 1 1 0 0 1
1 1 1 0 0
0 1 1 1 0
0 0 1 1 1
求第一个树相当于init*tmp
1表示在d范围内的附近,0表示不在
但是矩阵快速幂是O(n^3*logn)肯定要超时
所以把tmp:1 1 0 0 1 O(n^2*logn)
两个程序最重要的核心是:

for(i=0;i<n;++i)for(c[i]=j=0;j<n;++j)c[i]+=a[j]*b[i>=j?(i-j):(n+i-j)]%m;

本来应该是init * tmp*****tmp(k次)
由于矩阵乘法的结合律所以改写成init*(tmp*****tmp)
比如
a b
1 1
1 1
0 0
0 0
1 1
当i=0求第一个位置,
a[0]*b[0]
a[1]*b[4]
a[2]*b[3]
a[3]*b[2]
a[4]*b[1]

他是一个环,a是顺着的,b是反着的
同理当i=1时
a[0]*b[1]
a[1]*b[0]
a[2]*b[4]
a[3]*b[3]
a[4]*b[2]

总结:行走的结题报告真的666666666

第三题:最简单的题

时间
1s
题目描述
给出正整数n和k,计算j(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值,其中k mod i表示k除以i的余数。
例如j(5, 3)=3 mod 1 + 3 mod 2 + 3 mod 3 + 3 mod 4 + 3 mod 5=0+1+0+3+3=7
输入
第一行2个数n,k。
输出
一个数 j(n,k)。
样例
Input Output
5 3 7

Output
7

数据范围
50% 数据 满足 n<1e6
100%数据 满足 n<1e12 k<=n

解题思路:
比如n=36,k=36
36/?=
1~4 5 6 7 8 9 10~12 13~18 ……
0 7 6 5 5 4 3 2 ……
同理把36%?=
写出来会发现以上面那个分成几个区间,区间中的余数为等差数列
当i>k时,余数都为k
所以sum=n*k,然后再用区间分段减去等差数列的和

核心代码

for(LL i=1;i<=n;i=r+1){        j=m/i,l=m/(j+1)+1,r=m/j;        if(r>n)r=n;        sum-=(LL)(l+r)*j*(r-l+1)/2;    }

具体代码

#include<iostream>#include<cstdio>#define LL long longusing namespace std;LL n,m,sum;int main(){    freopen("C.in","r",stdin);    freopen("C.out","w",stdout);    cin>>n>>m;    sum=n*m;    if(n>=m)n=m;    LL j,l,r;    for(LL i=1;i<=n;i=r+1){        j=m/i,l=m/(j+1)+1,r=m/j;        if(r>n)r=n;        sum-=(LL)(l+r)*j*(r-l+1)/2;    }    cout<<sum<<endl;    return 0;}

行走的结题报告66666666666666!

0 0