2022NOIPA层联训3 ABCD
T1简单题意转化没有想到,甚至正解考试后都死磕半天硬是绕不过弯;T2DP考场想过但是根本不会预设,连正确的DP式子都没想到;T4构造也是瞎弄一番。还能走多久?每天嚷嚷几句就能真的进步吗?多跑几步就真的可以超越什么吗?到底该怎么做,怎么思考,怎么拼,才能有进步,才是对的?
服输是不可能的,既然有挫败,自然要反思:(1)关于套路的题目,你认真总结了吗?(2)需要灵活思考的东西,平时改题有没有多想多试?(3)改题究竟有没有自己好好想,好好分析样例,一步一步理解题目,写代码,看代码,学暴力,学多解。(4)考试的时候:能不能从暴力思维里跳出,认真分析题目,本质和特性,套路和方法,先想暴力,再从暴力一步步推到正解也许是对的,但是容易陷入各种鸡毛蒜皮的优化,实际上没什么屁用,还是从简化问题,抽离模型的角度思考比较好
T1[数据结构:线段树+题目转化]给出序列A,主角是a1,m个操作,每个给出ki个数,从A中删除ki个,加入给出的数。给出Q次修改,有继承关系,问每次修改之后主角是否会被删除。(n<=1e5)
容易想到ai<a1和ai>a1是唯一有用的表示,把小于用0表示,大于用1表示,问题转化成是否存在某次操作时0的个数<del_cnt。发现0_cnt-del_cnt可以表示出主角是否会在本次被删除,于是想要线段树维护每个操作之后0的个数,如果定义0是<a1,1是>=a1,把a1包含进去,在修改的时候需要考虑后面的是否会被修改到,因为当cnt_0<del_cnt时,把1变成0,不会实际改变下一个序列的0和1的个数,也就是需要修改一段前缀截止cnt_0-del_cnt>=0.可以分块找,散块确定位置,很麻烦。
修改一下定义:0是<a1,1是>a1,维护a1的排名,发现只要一次0<-->1的交换一定会改变一段后缀,所以直接维护不用找位置,排名也许会有负数,但是实际意义就是还要加进来几个才可以合法,后面会正确加回去。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
ll x=0,h=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')h=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*h;
}
#define int ll
const int N=1e5+100;
int n,m,Q;
int mi[N<<3],tag[N<<3];
int a[N],A,top[N],cha[N];
vector<int>chg[N];
#define lson (rt<<1)
#define rson (rt<<1|1)
inline void Build(int rt,int l,int r)//区间加减,单点查询
{
if(l==r)mi[rt]=cha[l];
else
{
int mid=(l+r)>>1;
Build(lson,l,mid);Build(rson,mid+1,r);
mi[rt]=min(mi[lson],mi[rson]);
}
}
inline void Pd(int rt)
{
if(!tag[rt])return;
tag[lson]+=tag[rt];
tag[rson]+=tag[rt];
mi[lson]+=tag[rt];
mi[rson]+=tag[rt];
tag[rt]=0;
}
inline void Insert(int rt,int l,int r,int L,int R,int vl)
{
if(L>R)return;
if(L<=l&&r<=R)
{
tag[rt]+=vl;
//chu("from:%lld\n",mi[rt]);
mi[rt]+=vl;//chu("mi[%d--%d]+=%d mi:%lld\n",l,r,vl,mi[rt]);
return;
}
int mid=(l+r)>>1;
Pd(rt);
if(L<=mid)Insert(lson,l,mid,L,R,vl);
if(R>mid)Insert(rson,mid+1,r,L,R,vl);
mi[rt]=min(mi[lson],mi[rson]);
//chu("mi[%d]=min[%d]:%lld,min[%d]:%lld\n",rt,lson,mi[lson],rson,mi[rson]);
}
// inline int Query(int rt,int l,int r,int L,int R)
// {
// if(L<=l&&r<=R)return mi[rt];
// Pd(rt);
// int mid=(l+r)>>1;
// int ans=1e9;
// if(L<=mid)ans=Query(lson,l,mid,L,R);
// if(R>mid)ans=min(ans,Query(rson,mid+1,r,L,R));
// return ans;
// }
signed main()
{
// freopen("a1.in","r",stdin);
//freopen("1.out","w",stdout);
n=re(),m=re(),Q=re();
_f(i,1,n)a[i]=re();
A=a[1];
int minn=0;//维护小于A的个数,大于A的个数
_f(i,1,n)minn+=(a[i]<A);
_f(i,1,m)
{
top[i]=re();
cha[i]=minn+1-top[i];//维护A的排名-del
minn-=top[i];
_f(j,1,top[i])
{
int x=re();
chg[i].push_back(x);
minn+=(x<A);
}
}
//_f(i,1,m<<2)mi[i]=1e17;
//_f(i,1,m)chu("cha[%d]:%d\n",i,cha[i]);
Build(1,1,m);
// _f(i,1,m<<2)chu("mi[%d]:%lld\n",i,mi[i]);
// chu("mi:%d\n",mi[1]);
_f(i,1,Q)
{
int year=re(),pos=re(),val=re();
//chu("val:%lld chg[%d][%d]:%lld\n",val,year,pos-1,chg[year][pos-1]);
if(val>A&&chg[year][pos-1]<A)//+1
{
//chu("add 0!!!!!!!!!!!!!!%d--%d -1\n",year+1,m);
Insert(1,1,m,year+1,m,-1);
}
else if(val<A&&chg[year][pos-1]>A)
{
//chu("add 1%d--%d 1\n",year+1,m);
Insert(1,1,m,year+1,m,1);
}
chg[year][pos-1]=val;
//chu("query:%d mi[1]:%d\n",i,mi[1]);
if(mi[1]<=0)
{
chu("0\n");
}
else chu("1\n");
// _f(j,1,5)chu("%lld ",mi[j]);
// chu("\n\n");
}
return 0;
}
/*
5 3 3
50 40 30 20 10
4 1 2 3 100
1 4
2 6 7
1 3 300
2 1 400
2 1 5
*/
T2【预设型DP】给出n个数的序列进行染色,如果ai-1和ai+1已经有颜色ai就自动有色。求染色染全方案数。(n<=400)
\(dp[i][j]:代表前i个数已经有色,有j个连续段的方案数,考虑再染色一个位置可以染色那些,因为染色顺序不同方案不同,所以本次染色就算是染了之前的位置的,也是不同方案\)
点击查看代码
//吾必胜
#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
ll x=0,h=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')h=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*h;
}
//#define int ll
const int N=300+10;
int dp[410][410],mod,n;
inline void upd(int&a,ll b)
{
a=((ll)a+(ll)b)%mod;
}
int main()
{
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
n=re(),mod=re();
dp[0][0]=1;
_f(i,1,n)
{
_f(j,1,n)
{
upd(dp[i][j],dp[i-1][j-1]*1ll*j);//自己一个段,多开1个,多一个段
upd(dp[i][j],dp[i-1][j]*2ll*j);//插入(1),多开1个,段不变
if(i>=2)upd(dp[i][j],dp[i-2][j]*2ll*j);//插入(2),多开2个,段不变
if(i>=2)upd(dp[i][j],dp[i-2][j+1]*2ll*j);//合并(1),隔着2个,多开2个,段少一个,有2*j种选法
if(i>=3)upd(dp[i][j],dp[i-3][j+1]*1ll*j);//合并(2),隔着3个,多开3个,段少一个,j种训法
}
}
chu("%d",dp[n][1]);
return 0;
}
/*
*/
T4[树上构造题目]要求构造一棵树,给出每个编号连续的点集合的连通性。(n<=2000)
对于特殊的情况,只有[l,r]和[i,i]可以联通,可以有固定的特殊构造,(n<=3无解)
对于不特殊的情况,定义好区间【l,r】是[l,r]序号集合联通,如果出现[l,r]包含了[l,x1]、[x1+1,x2]、[x2+1,]...[xk+1,r]的极长好区间,那么可以类似特殊构造,这是区间不相交只包含的情况,注意连边必须要求如果从左往右扫,那么边是连接端点,可以贪心理解,尽量不出现在考虑范围外的连续联通块。
如果出现交,发现不影响答案,忽略就行。具体的,枚举右端点,从小区间到大区间拓展。
点击查看代码
<details>
<summary>点击查看代码</summary>
//吾必胜
include<bits/stdc++.h>
using namespace std;
define _f(i,a,b) for(register int i=a;i<=b;++i)
define f_(i,a,b) for(register int i=a;i>=b;--i)
define chu printf
define ll long long
define rint register int
define ull unsigned long long
inline ll re()
{
ll x=0,h=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch'-')h=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*h;
}
const int N=2020;
char s[N][N];
int n,fa[N];
int main()
{
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
n=re();
_f(i,1,n)scanf("%s",s[i]+i),fa[i]=i;
f(i,1,n)
f(j,i-1,1)
if(s[j][i]'1'&&fa[i]>j)
{
chu("%d %d\n",i,j);
if(fa[fa[i]-1]>j)
{
chu("%d %d\n",j,fa[i]-1);
f_(k,fa[fa[i]-1]-1,j+1)if(fa[k]==k)chu("%d %d\n",k,i);
}
_f(k,j,i)fa[k]=fa[j];
}
return 0;
}
/*
12
100100000001
11100000001
1000000000
100000000
10010001
1110000
100000
10000
1001
111
10
1
*/
</details>
浙公网安备 33010602011771号