20180804的Test
T1 偏差
【题目描述】
给出一个序列A,再给出一段序列B,对于1≤i≤m,可能有B[i]=A[L+i-1]+k,我们称k为这个偏差值,有5个询问:
- 偏差值k有几种可能
- 偏差值|k|的最小值是多少
- L有几种取值方案
- L的最小值是多少
- L的最大值是多少
【输入格式】
多组数据
第一行一个正整数 n,表示序列 A 的长度。
第二行 n 个用空格隔开的非负整数 A[1] . . . A[n],描述了序列 A。
第三行一个正整数 m,表示序列 B 的长度。
第四行 m 个用空格隔开的非负整数 B[1] . . . B[m],描述了序列 B。
【输出格式】
对于每组数据,输出 5 个用空格隔开的整数,依次表示 5 个问题的答案。特别地,对于问题 2, 4, 5,如果无解,请输出 0 作为答案。
【数据范围与约定】
\(1 \leq n , m \leq 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 意义下的结果。
【数据范围与约定】
$ 1\leq k\leq m\leq 10^5 $
题解
若是考虑每一种插旗情况,那实在有点多啊--||,肯定会TLE,因此我们要换一个思路,考虑每一条边。这条边被用到,当且仅当左边的子树有旗帜且右边的子树也有旗帜,故这条边对答案的贡献为:(a代表这条边左子树的点的个数)$ w_i*\sumk(C_aiC_{n-a}^{k-i})\(
但是这个方法的时间复杂度仍然不理想,我们考虑正难则反,可容易推得公式:\) w_i(C_nk-C_ak-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\),求至少需要几个矩形
【输入格式】
本题包含多组数据。第一行一个整数 T,表示数据组数。接下来依次描述各组数据,
对于每组数据:
第一行 2 个整数 n, S,表示n个点,矩形面积必须为S。
接下来 n 行,每行 2 个非负整数 x, y,描述每个点的横纵坐标。
【输出格式】
对于每组数据,一行一个整数表示所需要使用的最少的矩形数目。
【数据范围与约定】
\(n\leq 100 ,x\leq3*10^6 , 1\leq y,S\leq2*10^5\)
题解
先把坐标离散化。
首先得知道一个结论:最优解中任意两个矩形的横坐标只可能是相离或包含,不可能是相交。证明略。
显然区间DP是个不错的选择。
设\(f_{l,r,h}\)为覆盖横坐标\(l∼r\),纵坐标>h的所有矩形需要的最少次数。
枚举l,r,h,有两种转移:找到一个横坐标i,使得没有任意一个矩形穿过i。枚举i分治即可。放一个横坐标为\(l∼r\)的矩形,把高度设为上限。
对于每一个h,这一层的转移是\(O(n^3)\)的,到下一层的转移是\(O(n^2\log n)\) 的,所以总时间复杂度就是\(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(一闪而现),但是思路被我一秒枪毙(感觉不对+写不出来)...终究还是只能归结于太嫩了,题目做得太少。
心得:
题目还是要多做啊,拓宽视野,算法的不足也要及时补上,加油吧。
正因为生命有限,所以才显得更重要,正因为生命有限,所以才更应该努力不懈。