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

第一题:一到简单题

题目描述
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 long
using 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 long
using 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 long
using 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值