20180804的Test

T1 偏差

【题目描述】

给出一个序列A,再给出一段序列B,对于1≤i≤m,可能有B[i]=A[L+i-1]+k,我们称k为这个偏差值,有5个询问:
1. 偏差值k有几种可能
2. 偏差值|k|的最小值是多少
3. L有几种取值方案
4. L的最小值是多少
5. L的最大值是多少

【输入格式】

多组数据
第一行一个正整数 n,表示序列 A 的长度。
第二行 n 个用空格隔开的非负整数 A[1] … A[n],描述了序列 A。
第三行一个正整数 m,表示序列 B 的长度。
第四行 m 个用空格隔开的非负整数 B[1] … B[m],描述了序列 B。

【输出格式】

对于每组数据,输出 5 个用空格隔开的整数,依次表示 5 个问题的答案。特别地,对于问题 2, 4, 5,如果无解,请输出 0 作为答案。

【数据范围与约定】

1n,m105 1 ≤ n , m ≤ 10 5


题解

有了这个k的存在,让我们很头疼。要是没有这个k该多好,来一个KMP就可以轻松A掉了!因此我们需要把这个k给消掉,你只需要动用你聪明的小脑瓜,使用“瞪眼法”,就会有这一个想法:我们可不可以将A和B序列中相邻的两个数相减,那么这样k的影响就被消去了。此时在来一个KMP就可以搞定啦~~显然这是对的。那么我们就开始码代码了

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N=1000005;
const int INF=2*1e9+7;
int n,m,a[N],b[N],p[N],x[N],y[N],x1,x2=INF,x3,x4=INF,x5=-INF,n1,tb[N];
int main()
{
    int T;
    scanf("%d",&T);
    while (T--){
        scanf("%d",&n);
        memset(p,0,sizeof(p)); x1=0; x2=INF; x3=0; x4=INF; x5=-INF; n1=0;
        for (int i=1; i<=n; i++) scanf("%d",&a[i]); n--;
        for (int i=1; i<=n; i++) x[i]=a[i+1]-a[i];
        scanf("%d",&m);
        for (int i=1; i<=m; i++) scanf("%d",&b[i]); m--;
        for (int i=1; i<=m; i++) y[i]=b[i+1]-b[i];
        if (m!=0){
            p[1]=0; int j=0;
            for (int i=1; i<m; i++){
                while (j>0 && y[j+1]!=y[i+1]) j=p[j];
                if (y[j+1]==y[i+1]) j++;
                p[i+1]=j;
            }
            j=0;
            for (int i=0; i<n; i++){
                while (j>0 && y[j+1]!=x[i+1]) j=p[j];
                if (y[j+1]==x[i+1]) j++;
                if (j==m){
                    j=p[j];
                    tb[++n1]=a[i-m+2]-b[1];
                    x2=min(x2,abs(a[i-m+2]-b[1]));
                    x3++; x4=min(x4,i-m+2); x5=max(x5,i-m+2);
                }
            }
            sort(tb+1,tb+1+n1);
            x1=unique(tb+1,tb+1+n1)-tb-1;
            if (x2==INF) x2=0; if (x4==INF) x4=0; if (x5==-INF) x5=0;
            printf("%d %d %d %d %d\n",x1,x2,x3,x4,x5);  
        }
        else {
            m++; n++;
            for (int i=1; i<=n; i++) x2=min(x2,abs(b[1]-a[i]));
            x3=n; x4=1; x5=n;
            sort(a+1,a+1+n);
            x1=unique(a+1,a+1+n)-a-1;
            printf("%d %d %d %d %d\n",x1,x2,x3,x4,x5);  
        }
    }
    return 0;
}

T2 闪烁魔法

【题目描述】

给一颗树,有边权,有k个插有旗帜的点,需要访问所有插有旗帜的点,且要回到出发点(出发点任选),而且每次访问都是最优的方案,插旗帜的方案是等概率的,求可能情况代价的平均值

【输入格式】

本题包含多组数据,第一行一个正整数 T,表示数据组数。接下来依次描述每组数据,对于每组数据:
第一行 2 个正整数 n, k,分别表示点数,以及插有旗帜的地点的数目。
接下来 n − 1 行,每行 3 个正整数 u,v,w,表示一条边,连接u,v,边权为w

【输出格式】

对于每组数据,输出一行一个正整数,表示期望消耗的魔法值在模998244353 意义下的结果。

【数据范围与约定】

1km105 1 ≤ k ≤ m ≤ 10 5


题解

若是考虑每一种插旗情况,那实在有点多啊-_-||,肯定会TLE,因此我们要换一个思路,考虑每一条边。这条边被用到,当且仅当左边的子树有旗帜且右边的子树也有旗帜,故这条边对答案的贡献为:(a代表这条边左子树的点的个数) wiki=1(CiaCkina) w i ∗ ∑ i = 1 k ( C a i ∗ C n − a k − i )
但是这个方法的时间复杂度仍然不理想,我们考虑正难则反,可容易推得公式: wi(CknCkaCkna) w i ∗ ( C n k − C a k − C n − a k )
接下来就可以愉快的码代码啦~~~

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define N 300005
#define int long long
using namespace std;
const int mod=998244353;
int n,m,ans,sum[N],head[N],jc[N],jc_inv[N],edgenum=0,vet[N],dis[N],next[N];
void addedge(int u,int v,int t)
{
    vet[++edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
    dis[edgenum]=t;
}
int inv(int x)
{
    if (x==1) return 1;
    return (mod-mod/x)*inv(mod%x)%mod;
}
void init(int n)
{
    memset(head,-1,sizeof head); edgenum=ans=0;
    jc[0]=1;
    for (int i=1; i<=n; i++) jc[i]=jc[i-1]*i%mod;
    jc_inv[n]=inv(jc[n]);
    for (int i=n-1; i>=0; i--) jc_inv[i]=jc_inv[i+1]*(i+1)%mod;
}
int C(int x,int y)
{
    if (x<y) return 0;
    return jc[x]*jc_inv[y]%mod*jc_inv[x-y]%mod;
}
void calc(int k,int x,int y)
{
    int tmp=((C(n,m)-C(x,m)-C(y,m))%mod+mod)%mod;
    ans=(ans+tmp*k)%mod;
}
void dfs(int u,int fa)
{
    sum[u]=1;
    for (int e=head[u],v; e!=-1; e=next[e])
        if ((v=vet[e])!=fa){
            dfs(v,u); sum[u]+=sum[v];
            calc(dis[e],sum[v],n-sum[v]);
        }
}
signed main(){
    int T;
    scanf("%lld",&T);
    while (T--){
        scanf("%lld%lld",&n,&m);
        init(n);
        for (int i=1; i<n; i++){
            int x,y,z;
            scanf("%lld%lld%lld",&x,&y,&z);
            addedge(x,y,z); addedge(y,x,z);
        }
        dfs(1,0);
        ans=ans*2%mod*inv(C(n,m))%mod;
        printf("%lld\n",ans);
    }
    return 0;
}   

T3 景中人

【题目描述】

给n个整点(横纵坐标均为非负整数),要用矩形把其都覆盖住(确定矩形面积),且每个矩形要贴着直线 y=0 y = 0 ,求至少需要几个矩形

【输入格式】

本题包含多组数据。第一行一个整数 T,表示数据组数。接下来依次描述各组数据,
对于每组数据:
第一行 2 个整数 n, S,表示n个点,矩形面积必须为S。
接下来 n 行,每行 2 个非负整数 x, y,描述每个点的横纵坐标。

【输出格式】

对于每组数据,一行一个整数表示所需要使用的最少的矩形数目。

【数据范围与约定】

n100,x3106,1y,S2105 n ≤ 100 , x ≤ 3 ∗ 10 6 , 1 ≤ y , S ≤ 2 ∗ 10 5


题解

先把坐标离散化。
首先得知道一个结论:最优解中任意两个矩形的横坐标只可能是相离或包含,不可能是相交。证明略。
显然区间DP是个不错的选择。
fl,r,h f l , r , h 为覆盖横坐标 lr l ∼ r ,纵坐标>h的所有矩形需要的最少次数。
枚举l,r,h,有两种转移:找到一个横坐标i,使得没有任意一个矩形穿过i。枚举i分治即可。放一个横坐标为 lr l ∼ r 的矩形,把高度设为上限。
对于每一个h,这一层的转移是 O(n3) O ( n 3 ) 的,到下一层的转移是 O(n2logn) O ( n 2 log ⁡ n ) 的,所以总时间复杂度就是 O(n4) O ( n 4 )

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int INF=2*1e9+7;
const int N=105;
struct node {int x,y;}a[N];
int n,s,f[N][N][N],tx[N],ty[N],n1,n2,id[N];
int calc(int x)
{
    if (x) return s/x;
    return INF;
}
int dfs(int l,int r,int h)
{
    int &s=f[h][l][r];
    if(~s) return s;
    while(l<=r && id[l]<=h) l++;
    while(l<=r && id[r]<=h) r--;
    if (l>r) return s=0;
    s=INF;
    for(int i=l; i<r; i++) s=min(s,dfs(l,i,h)+dfs(i+1,r,h));
    int hh=calc(tx[r]-tx[l]);
    if (hh<=ty[h]) return s;
    int v=upper_bound(ty+1,ty+n2+1,hh)-ty-1;
    s=min(s,dfs(l,r,v)+1);
    return s; 
}
int main()
{
    int T;
    scanf("%d",&T);
    while (T--) {
        scanf("%d%d",&n,&s);
        for(int i=1; i<=n; i++){
            scanf("%d%d",&a[i].x,&a[i].y);
            tx[i]=a[i].x; ty[i]=a[i].y;
        }
        sort(tx+1,tx+n+1); sort(ty+1,ty+n+1);
        n1=unique(tx+1,tx+n+1)-tx-1; n2=unique(ty+1,ty+n+1)-ty-1;
        memset(f,-1,sizeof f);
        for(int i=1; i<=n1; i++) id[i]=0;
        for(int i=1; i<=n; i++){
            a[i].x=lower_bound(tx+1,tx+n1+1,a[i].x)-tx;
            a[i].y=lower_bound(ty+1,ty+n2+1,a[i].y)-ty;
            id[a[i].x]=max(id[a[i].x],a[i].y);
        }
        int ans=dfs(1,n1,0);
        printf("%d\n",ans);
    }
    return 0;
}

总结:

今天LOJ大吉,洛谷大凶,真是矛盾啊…为我脸黑埋伏笔这里写图片描述
这里写图片描述
这估计也是注定了我要么A掉,要么爆零的脸黑结果吧
这套题,说难不难,说简单不简单,镇中大佬集体RP爆降是我排名虚高的原因。不能因此而嘚瑟

T1 100/100

这道题,看题3min就出正解,虽说KMP稍有忘却,但还是可以1h AC,稳!!

T2 0/100

这道题,暴力居然WA了,如此脸黑的我也很无奈啊╮(╯▽╰)╭,后面急于写正解,也没有时间去过多关注如何WA的,估计是手抖吧,但是没有想到基于边的算法,这说明思路过于局限,不过之前也从未做过此类题,情有可原。

T3 0/100

唔,错误贪心,我也是走投无路啊,曾经想到过DP(一闪而现),但是思路被我一秒枪毙(感觉不对+写不出来)…终究还是只能归结于太嫩了,题目做得太少。

心得:

题目还是要多做啊,拓宽视野,算法的不足也要及时补上,加油吧。
正因为生命有限,所以才显得更重要,正因为生命有限,所以才更应该努力不懈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值