2021.11.5考试总结[冲刺NOIP模拟23]
T1 回文
转化成从 \((1,1)\) 和 \((n,m)\) 同时开始走,坐标 \(DP\) 。
走的步数奇偶不同,统计答案的方式略有不同。
\(code:\)
T1
#include<bits/stdc++.h>
using namespace std;
namespace IO{
#define fi first
#define se second
#define pb push_back
#define mpr make_pair
typedef double DB; typedef long double LD;
typedef long long LL; typedef unsigned long long ULL;
typedef pair<int,int>PII;
int read(){
LL x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
void write(LL x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;
const int NN=510,mod=993244853;
int n,m,ext,ans,mat[NN][NN];
int f[2][NN][NN];
char ch[NN];
signed main(){
freopen("palin.in","r",stdin);
freopen("palin.out","w",stdout);
n=read(); m=read(); ext=(n+m-2)>>1;
for(int i=1;i<=n;i++){
scanf("%s",ch+1);
for(int j=1;j<=m;j++) mat[i][j]=ch[j]-'a';
}
if(mat[1][1]!=mat[n][m]){ puts("0"); exit(0); }
f[0][1][n]=1;
for(int i=0;i<ext;i++){
int lmx=min(n,i+1),lmy=max(1,n-i);
memset(f[i+1&1],0,sizeof(f[i+1&1]));
for(int j=1;j<=lmx;j++){
int x=i-j+2;
for(int k=n;k>=lmy;k--){
int y=n+m-k-i;
if(x<m&&y>1) if(mat[j][x+1]==mat[k][y-1]) (f[i+1&1][j][k]+=f[i&1][j][k])%=mod;
if(x<m&&k>1) if(mat[j][x+1]==mat[k-1][y]) (f[i+1&1][j][k-1]+=f[i&1][j][k])%=mod;
if(j<n&&y>1) if(mat[j+1][x]==mat[k][y-1]) (f[i+1&1][j+1][k]+=f[i&1][j][k])%=mod;
if(j<n&&k>1) if(mat[j+1][x]==mat[k-1][y]) (f[i+1&1][j+1][k-1]+=f[i&1][j][k])%=mod;
}
}
}
for(int i=1;i<=n;i++){
(ans+=f[ext&1][i][i])%=mod;
if((n+m&1)&&i<n) (ans+=f[ext&1][i][i+1])%=mod;
}
write(ans,'\n');
return 0;
}
T2 快速排序
分析给出的代码,找出排序的本质。
从左往右扫描,遇到 nan
直接填到答案中,遇到数 \(x\) 则找到它之后所有未计入答案的数,排序后计入答案。
排个序,单调指针。
\(code:\)
T2
#include<bits/stdc++.h>
using namespace std;
namespace IO{
typedef double DB; typedef long double LD;
typedef long long LL; typedef unsigned long long ULL;
int read(){
LL x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
void write(LL x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;
const int NN=500010;
int t,n,mx,now,nows;
vector<int>sot;
namespace Number_IO{
struct number{
bool is;
int v;
number(){ is=1; v=0; }
number(int x){ is=0; v=x; }
}num[NN],ans[NN];
number Read(){
int x=0,it=0; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='n'){ getchar(); getchar(); return number(); } ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return number(x);
}
void Write(number x,char sp){
char ch[20]; int len=0,v=x.v;
if(x.is){ putchar('n'); putchar('a'); putchar('n'); putchar(sp); return; }
do{ ch[len++]=v%10+'0'; v/=10; }while(v);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
} using namespace Number_IO;
signed main(){
freopen("qsort.in","r",stdin);
freopen("qsort.out","w",stdout);
t=read();
while(t--){
n=read(); mx=now=nows=0;
sot.clear();
for(int i=1;i<=n;i++){
num[i]=Read();
if(!num[i].is) sot.push_back(num[i].v);
}
sort(sot.begin(),sot.end());
for(int i=1;i<=n;i++)
if(num[i].is) ans[++now]=num[i];
else if(num[i].v<mx) continue;
else{
while(sot[nows]<num[i].v) ans[++now]=number(sot[nows++]);
ans[++now]=sot[nows++]; mx=num[i].v;
}
for(int i=1;i<=n;i++) Write(ans[i],i==n?'\n':' ');
}
return 0;
}
T3 混乱邪恶
构造方案:先 \(-1\) 与 \(1\) 交替,之后将排序后的 \(a_{2i}\) 与 \(a_{2i-1}\) 分为一组并作差,将差值从大到小扫描,通过交换同一组元素的答案来调整两边的和。
证明一定有解:
证明正确性: link
\(code:\)
T3
#include<bits/stdc++.h>
using namespace std;
namespace IO{
#define fi first
#define se second
#define pb push_back
#define mpr make_pair
typedef double DB; typedef long double LD;
typedef long long LL; typedef unsigned long long ULL;
typedef pair<int,int>PII;
int read(){
LL x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
void write(LL x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;
const int NN=1000010;
int n,m,sum,ans[NN];
vector<int>vec[NN];
struct Data{
int id,vl;
bool operator<(const Data& tmp)const{
return vl<tmp.vl;
}
}a[NN];
signed main(){
freopen("chaoticevil.in","r",stdin);
freopen("chaoticevil.out","w",stdout);
n=read(); m=read();
for(int i=1;i<=n;i++) a[i].id=i,a[i].vl=read();
puts("NP-Hard solved");
sort(a+1,a+n+1);
for(int i=1+(n&1);i<=n;i+=2){
sum+=a[i+1].vl-a[i].vl;
vec[a[i+1].vl-a[i].vl].push_back(i);
ans[a[i+1].id]=1; ans[a[i].id]=-1;
}
if(n&1) sum-=a[1].vl,ans[a[1].id]=-1;
for(int i=m;i;i--) if(vec[i].size()&&sum>=2*i)
for(int x:vec[i]){
sum-=2*i;
swap(ans[a[x].id],ans[a[x+1].id]);
if(sum<2*i) break;
}
for(int i=1;i<=n;i++) write(ans[i],' ');
return puts(""),0;
}
T4 校门外歪脖子树上的鸽子
将每次修改抽象成一条链,第一个断开链的点将这个区间分为两段,左边一段是树上靠左区间的一段后缀,对链上所有点的右儿子有贡献,同理,右边一段对所有左儿子有贡献。
于是树剖,将一个点的贡献存在它的兄弟上,每次找到链的位置进行修改或查询。
边界恶心的离谱。
\(code:\)
T4
#include<bits/stdc++.h>
#define int long long
using namespace std; namespace IO{ int read(){ int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } void write(int x,char sp){ char ch[20]; int len=0; if(x<0) x=-x,putchar('-'); do{ ch[len++]=x%10+'0'; x/=10; }while(x); for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp); } } using namespace IO;
const int NN=400010;
int n,m,ext,root,L[NN],R[NN],sn[NN][2],fa[NN],len[NN],lp[NN],rp[NN];
map<int,int>mp[NN];
bool pos(int x){ return x==sn[fa[x]][1]; }
int bro(int x){ return x==root?root:sn[fa[x]][pos(x)^1]; }
void dfs(int s){
L[s]=R[s]=s;
if(sn[s][0]) dfs(sn[s][0]),L[s]=L[sn[s][0]];
if(sn[s][1]) dfs(sn[s][1]),R[s]=R[sn[s][1]];
if(L[s]>R[s]) swap(sn[s][0],sn[s][1]),L[s]=L[sn[s][0]],R[s]=R[sn[s][1]];
len[s]=R[s]-L[s]+1; mp[L[s]][R[s]]=s;
}
namespace Tree_Chain{
int cnt,id[NN],dep[NN],dfn[NN],siz[NN],son[NN],top[NN];
void dfs1(int s){
dep[s]=dep[fa[s]]+1; siz[s]=1;
if(s==root) lp[s]=rp[s]=s;
else{
lp[s]=pos(s)?s:lp[fa[s]];
rp[s]=pos(s)?rp[fa[s]]:s;
}
if(sn[s][0]) dfs1(sn[s][0]),siz[s]+=siz[sn[s][0]];
if(sn[s][1]) dfs1(sn[s][1]),siz[s]+=siz[sn[s][1]];
son[s]=siz[sn[s][0]]>siz[sn[s][1]]?sn[s][0]:sn[s][1];
}
void dfs2(int s,int t){
dfn[s]=++cnt; id[cnt]=s; top[s]=t;
if(!son[s]) return;
dfs2(son[s],t);
(son[s]==sn[s][0])?dfs2(sn[s][1],sn[s][1]):dfs2(sn[s][0],sn[s][0]);
}
int LCA(int x,int y){
while(top[x]!=top[y]) dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
return dep[x]<dep[y]?x:y;
}
int rtson(int x,int y){
x=top[x];
while(x!=top[y])
if(fa[x]==y) return x;
else x=top[fa[x]];
return son[y];
}
} using namespace Tree_Chain;
namespace Segment_Tree{
#define ld rt<<1
#define rd (rt<<1)|1
struct segment_tree{
int w[NN<<2],tag[NN<<2],sum[NN<<2];
void pushup(int rt){ sum[rt]=sum[ld]+sum[rd]; }
void down(int rt,int val){ sum[rt]+=val*w[rt]; tag[rt]+=val; }
void pushdown(int rt){ if(tag[rt]) down(ld,tag[rt]),down(rd,tag[rt]),tag[rt]=0; }
void build(int rt,int l,int r,int typ){
if(l==r){ w[rt]=(pos(bro(id[l]))!=typ)?len[bro(id[l])]:0; return; }
int mid=l+r>>1;
build(ld,l,mid,typ); build(rd,mid+1,r,typ);
w[rt]=w[ld]+w[rd];
}
void update(int rt,int l,int r,int opl,int opr,int val){
if(l>=opl&&r<=opr) return down(rt,val),void();
pushdown(rt);
int mid=l+r>>1;
if(opl<=mid) update(ld,l,mid,opl,opr,val);
if(opr>mid) update(rd,mid+1,r,opl,opr,val);
pushup(rt);
}
int query(int rt,int l,int r,int opl,int opr){
if(l>=opl&&r<=opr) return sum[rt];
pushdown(rt);
int mid=l+r>>1,res=0;
if(opl<=mid) res+=query(ld,l,mid,opl,opr);
if(opr>mid) res+=query(rd,mid+1,r,opl,opr);
return res;
}
void UPD(int x,int y,int val){
while(top[x]!=top[y]) update(1,1,ext,dfn[top[x]],dfn[x],val),x=fa[top[x]];
if(x!=y) update(1,1,ext,dfn[y]+1,dfn[x],val);
}
int ASK(int x,int y,int res=0){
while(top[x]!=top[y]) res+=query(1,1,ext,dfn[top[x]],dfn[x]),x=fa[top[x]];
if(x!=y) res+=query(1,1,ext,dfn[y]+1,dfn[x]);
return res;
}
}ls,rs;
} using namespace Segment_Tree;
void single_upd(int x,int v){
if(pos(x)) ls.update(1,1,ext,dfn[bro(x)],dfn[bro(x)],v);
else rs.update(1,1,ext,dfn[bro(x)],dfn[bro(x)],v);
}
int single_ask(int x){
if(pos(x)) return ls.query(1,1,ext,dfn[bro(x)],dfn[bro(x)]);
return rs.query(1,1,ext,dfn[bro(x)],dfn[bro(x)]);
}
signed main(){
freopen("pigeons.in","r",stdin);
freopen("pigeons.out","w",stdout);
n=read(); m=read(); ext=2*n-1;
for(int i=1;i<n;i++){
sn[n+i][0]=read(); fa[sn[n+i][0]]=n+i;
sn[n+i][1]=read(); fa[sn[n+i][1]]=n+i;
}
for(int i=1;i<n;i++) if(!fa[n+i]) root=n+i;
dfs(root); dfs1(root); dfs2(root,root);
ls.build(1,1,ext,0); rs.build(1,1,ext,1);
while(m--){
int op=read(),x=read(),y=read(),qd,lca=LCA(x,y);
if(op==1){
qd=read();
if(mp[x].find(y)!=mp[x].end()){ single_upd(mp[x][y],qd); continue; }
int xx=lp[x],yy=rp[y];
if(dep[xx]<=dep[lca]) single_upd(rtson(x,lca),qd);
else single_upd(xx,qd), ls.UPD(xx,rtson(x,lca),qd);
if(dep[yy]<=dep[lca]) single_upd(rtson(y,lca),qd);
else single_upd(yy,qd), rs.UPD(yy,rtson(y,lca),qd);
} else{
if(mp[x].find(y)!=mp[x].end()){ write(single_ask(mp[x][y]),'\n'); continue; }
int xx=lp[x],yy=rp[y],res=0;
if(dep[xx]<=dep[lca]) res+=single_ask(rtson(x,lca));
else res+=single_ask(xx)+ls.ASK(xx,rtson(x,lca));
if(dep[yy]<=dep[lca]) res+=single_ask(rtson(y,lca));
else res+=single_ask(yy)+rs.ASK(yy,rtson(y,lca));
write(res,'\n');
}
}
return 0;
}