2021.8.25 提高组模拟赛
100+100+15 。第一题挺水,第二题花的时间太多了(一开始方向走错了),第三题真的没想到是个区间dp。悬吊吊的班一。
- T1 夏洛特
题面
⼄坂有宇为了收集全世界的能⼒,需要在进⾏旅⾏。
⼄坂有宇的初始坐标为(0,0)初始时间为0,他对⾃⼰的旅⾏做了 个计划,具体的说,对于\(1\le i\le n\),他希望在\(t_i\)时刻恰好到达坐标(\(x_i,y_i\))。对于⼀个时刻 ,如果坐标为(\(x,y\)),那么在
时刻,可以到达上下左右中的任意⼀个,但是不能停留在原地。
有宇希望知道他能不能达成他的计划。多组数据。
解法
按时间排序,然后看每一个点是否可以从上一个点走过来。“可以”的定义是时间“刚刚好够”或者比“刚刚好”的时间多偶数个时间单位。难度……还可以吧。
#include<cstdio>
#include<algorithm>
//#define zczc
using namespace std;
const int N=100010;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
wh*=f;return;
}
int m;
inline int abs(int s1){
return s1<0?-s1:s1;
}
struct point{
int t,x,y;
}a[N];
inline bool operator <(point s1,point s2){
return s1.t<s2.t;
}
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
int test;read(test);
while(test--){
read(m);
a[0].t=a[0].x=a[0].y=0;
for(int i=1;i<=m;i++){
read(a[i].t);
read(a[i].x);
read(a[i].y);
}
sort(a,a+m+1);
bool ok=true;
for(int i=1;i<=m;i++){
int dis=abs(a[i].x-a[i-1].x)+abs(a[i].y-a[i-1].y);
int nt=a[i].t-a[i-1].t;
if(nt>=dis&&(nt-dis&1)==0)continue;
ok=false;break;
}
if(ok)printf("Yes\n");
else printf("No\n");
}
return 0;
}
-T2 西⽐拉先知系统
题面
西⽐拉先知系统是⼀个强⼤的⼼灵指数监测⽹絡,能以声像扫描主动监控市⺠的⼼智与精神状态。为
了判定出更复杂的⼈类⼼理参数,西⽐拉系统纳⼊了不同于既存⼈类规范的超群⼈格──不会随意和他
⼈产⽣共鸣,也不会感情⽤事,能以⾮⼈类的眼光来俯瞰并裁定⼈类。
被纳⼊的超群⼈格会相互影响,共同处理数据。他们之间具体的影响⽅式形如⼀张⽆向图,如果你对
⼀个节点进⾏操作,和这个节点相邻的节点也会受到相同的影响。
操作有⼀种:使⼀个节点的权值加上\(x\)。
同时你还希望询问⼀个节点的权值(每⼀个节点的初始权值为0)。
解法
首先,暴力做法很好想,就是对每一个相邻点进行修改。但问题是这个做法可能会被卡,那就是当一个节点的度数很大时,修改的复杂度就会很高。于是考虑对“重点”(度比较多的点)和“轻点”进行分类讨论。
其实理论上来说我的做法也会被卡,但……数据比较水,放了我一马【开心】。
#include<cstdio>
#include<vector>
//#define zczc
using namespace std;
const int N=300010;
const int S=10000;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
wh*=f;return;
}
struct edge{
int t,next;
}e[N*2];
int esum,head[N];
inline void add(int fr,int to){
esum++;
e[esum].t=to;
e[esum].next=head[fr];
head[fr]=esum;
}
int m,n,q,d[N];
bool big[N];
vector<int>num[N];//记录每个点相邻的重点
int a[N],c[N];//加的数和(重点)的实际值
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
int op,s1,s2;
read(m);read(n);read(q);
for(int i=1;i<=n;i++){
read(s1);read(s2);
add(s1,s2);add(s2,s1);
d[s1]++,d[s2]++;
}
for(int i=1;i<=m;i++)big[i]=d[i]>S;
for(int i=1;i<=m;i++){
for(int j=head[i];j;j=e[j].next){
int th=e[j].t;
if(big[th])num[i].push_back(th);
}
}
while(q--){
read(op);
if(op){
read(s1);read(s2);
a[s1]+=s2;
for(int i=0;i<num[s1].size();i++){
c[num[s1][i]]+=s2;
}
}
else{
read(s1);
if(big[s1])printf("%d\n",a[s1]+c[s1]);
else{
int an=a[s1];
for(int i=head[s1];i;i=e[i].next){
an+=a[e[i].t];
}
printf("%d\n",an);
}
}
}
return 0;
}
-T3 替身使者
题面
众所周知,替⾝使者会互相吸引。
现在有\(n\)个替⾝使者,这些替⾝使者在⼀条很⻓的街道上活动。对于第\(i\)个替⾝使者,他可以在上第\(l_i\)个到第\(r_i\)个商铺中的⼀个停下来休息。
于是,⼀些替⾝使者就会处于同⼀个商铺。对于⼀个商铺,如果有\(x\)个替⾝使者到了这⼀家商铺,那么就会产⽣\(g(x)\)点吸引度,其中\(g(x)\)是⼀个关于\(x\)的多次函数(5次函数,1到5次项的系数输入,其它项为0),求一种使者停留方案使得所有商铺的吸引度最大。
解法
真的没想到竟然是个……区间dp加一点点贪心?满心以为是线性dp的我列了半天方程却然并卵,最后只能贪了15分,可悲啊……
它就是说,如果一个点当前可以有多个区间同时覆盖,那么就贪心地把所有能停留在此的使者全部拿来,再对于这个点左右两个区间进行操作。加上离散化即可。
由于自己太弱,怎么也卡不过最后十分,只好引用一下孙艺恒大佬的代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
int n,m,t[7],l[505],r[505],ans=0,vis[505],f[505],dp[505][505],cnt;
struct node
{
int name,data;
}a[505*2];
int cmp(node fi,node se)
{
return fi.data<se.data;
}
inline int lowbit(int x)
{
return -x&x;
}
void update(int x,int k)
{
while(x<=cnt)
{
f[x]+=k;
x+=lowbit(x);
}
}
int search(int x)
{
int num=0;
while(x)
{
num+=f[x];
x-=lowbit(x);
}
return num;
}
int g(int x)
{
return t[1]*x+t[2]*x*x+t[3]*x*x*x+t[4]*x*x*x*x+t[5]*x*x*x*x*x;
}
signed main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=5;i++)scanf("%lld",&t[i]);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&l[i],&r[i]);
a[i].name=i,a[i].data=l[i];
a[i+n].name=i+n,a[i+n].data=r[i];
}
sort(a+1,a+1+n*2,cmp);
a[0].data=-1;
for(int i=1;i<=n*2;i++)
{
if(a[i].data!=a[i-1].data)cnt++;
if(a[i].name<=n)l[a[i].name]=cnt;
else r[a[i].name-n]=cnt;
}
for(int i=1;i<=n;i++)if(l[i]==r[i])vis[l[i]]++;
for(int i=1;i<=cnt;i++)dp[i][i]+=g(vis[i]);
for(int p=2;p<=cnt;p++)
{
for(int i=1;i+p-1<=cnt;i++)
{
memset(f,0,sizeof(f));
int j=i+p-1;
for(int k=1;k<=n;k++)
{
if(l[k]>=i&&r[k]<=j)update(l[k],1),update(r[k]+1,-1);
}
dp[i][j]=max(dp[i][j-1]+g(search(j)),dp[i+1][j]+g(search(i)));
for(int k=i+1;k<j;k++)
{
dp[i][j]=max(dp[i][j],dp[i][k-1]+dp[k+1][j]+g(search(k)));
}
}
}
printf("%lld",dp[1][cnt]);
return 0;
}