Noip模拟90 2021.11.5(最长题解)
T1 回文
这种经典的\(dp\)就是不会做,就是不会。。。。。
除了\(dp\)数组不会设其他的都跟题解差不多,然而并没有啥用。。。。
设\(dp[len][x1][x2]\)表示回文串的一半长度为\(len\),从\((1,1)\)开始的串这时终点的横坐标为\(x1\),从\((n,m)\)开始的串终点的横坐标为\(x2\)
然后分情况转移即可
最后在一条对角线,也就是回文中心处统计答案
palin
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=501,mod=993244853;
namespace AE86{
FILE *wsn;
auto 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;
};
auto write=[](int x,char opt='\n'){
char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
};
}using namespace AE86;
int n,m,a[NN][NN],len;
int dp[2][NN][NN];
char s[NN];
namespace WSN{
inline short main(){
wsn=freopen("palin.in","r",stdin);
wsn=freopen("palin.out","w",stdout);
n=read(); m=read(); len=n+m-2>>1;
for(int i=1;i<=n;i++){scanf("%s",s+1);
for(int j=1;j<=m;j++)a[i][j]=s[j]-'a';
}
if(a[1][1]!=a[n][m]) return puts("0"),0;
dp[0][1][n]=1;
for(int i=0;i<len;i++){
int x1=min(n,i+1),x2=max(1ll,n-i);
memset(dp[i+1&1],0,sizeof(dp[i+1&1]));
for(int j=1;j<=x1;j++){
int y1=i-j+2;
for(int k=n;k>=x2;k--){
int y2=n+m-k-i;
if(j<n&&y2>1) if(a[j+1][y1]==a[k][y2-1]) (dp[i+1&1][j+1][k]+=dp[i&1][j][k])%=mod;
if(j<n&&k>1) if(a[j+1][y1]==a[k-1][y2]) (dp[i+1&1][j+1][k-1]+=dp[i&1][j][k])%=mod;
if(y1<m&&y2>1)if(a[j][y1+1]==a[k][y2-1]) (dp[i+1&1][j][k]+=dp[i&1][j][k])%=mod;
if(y1<m&&k>1) if(a[j][y1+1]==a[k-1][y2]) (dp[i+1&1][j][k-1]+=dp[i&1][j][k])%=mod;
}
}
}
int ans=0;
for(int i=1;i<=n;i++){
(ans+=dp[len&1][i][i])%=mod;
if((n+m&1)&&i<n) (ans+=dp[len&1][i][i+1])%=mod;
} write(ans);
return 0;
}
}
signed main(){return WSN::main();}
T2 快速排序
确实像大结论题,不过觉得考场上差点就分析出来了,可是最终还是没有。。。。
发现他排序的过程在以\(nan\)为\(left\)的时候,\(nan\)的相对位置不会变
所以直接扫一遍,扫到一个不是\(nan\)的数就把小于这个数的数全部输出
是\(nan\)直接输出\(nan\)
qsort
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int NN=5e5+5;
namespace AE86{
FILE *wsn;
auto read=[](){
int x=0;bool f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='a'){f=0,ch=getchar();break;}ch=getchar();}
if(!f) return -1ll;
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x;
};
auto write=[](int x,char opt='\n'){
char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
};
}using namespace AE86;
int T,n,stk[NN],top;
struct number{bool isnan;int value;}a[NN];
multiset<int> s;
auto solve=[](){
n=read(); s.clear();
for(int i=1;i<=n;i++){
int x=read();if(x>0)s.insert(x);
a[i]=x<0?number{1,x}:number{0,x};
}
for(int i=1;i<=n;i++){
if(a[i].isnan) printf("nan ");
else{
if(s.find(a[i].value)==s.end())continue;
multiset<int>::iterator it=s.begin();
while(*it<a[i].value){
stk[++top]=*it;
++it;
}
for(int j=1;j<=top;j++)
s.erase(s.find(stk[j])),printf("%lld ",stk[j]);
top=0; printf("%lld ",*it); s.erase(it);
}
}
puts("");
};
namespace WSN{
inline short main(){
wsn=freopen("qsort.in","r",stdin);
wsn=freopen("qsort.out","w",stdout);
T=read();while(T--)solve();
return 0;
}
}
signed main(){return WSN::main();}
T3 混乱邪恶
应\(OMA\)的委托,决定把这道题的题解写成论文。。。。。
\(\color{red}{\huge{Warning}}\)
考虑这道题的限制会把原来的数组变成什么样子
\(\{a_1,a_2,\dots,a_n\}\)是\(\{1,2,\dots,m\}\)的子集
这个保证了所有的\(a_i\)互不相同,并且值域确定了
\(\lfloor\frac{2m}{3}\rfloor <n\leq m\)
这个保证了\(a\)数组中一定有一串等差数列,并且公差为\(1\)
这个不难证明,因为在\([1,m]\)中最多只有\(\frac{m}{2}\)个偶数或者奇数,
如果保证两个数之间的差值都大于\(1\)显然是不可能的,因为\(n\in (\frac{2m}{3},m]\)
所以排完序之后一定有一段序列是公差为\(1\)的等差数列,即奇数偶数都有
保证\(\sum a_i\)为偶数
这个限制保证了\(a\)数组中一定有偶数个奇数
考虑如果没有限制的情况下,数组\(a\)有几种情况
不难发现,数组\(a\)要么都是偶数,要么有偶数个奇数,否则无法满足\(\sum a_i\in even\)
而前面的限制保证了\(a\)数组无法都是偶数,那么数组中只能有偶数个奇数
这样我们就分析出来了数组\(a\)构造上的几个性质:
- 所有数互不相同,值域在\([1,m]\)之间
- 一定有一串公差为一的等差数列
- 数组中一定有偶数个奇数
然后我们考虑如何构造。
如果数列长度为奇数,我们给他再加上一个元素\(0\),这样就保证了数组长度为偶数,以便我们后面分组
我们把数组\(a\)进行升序排序,然后相邻的两两为一组,并记录\(d_i=a_{2i}-a_{2i-1}\)
然后我们记录\(sm=\sum d_i\),这个值是我们“预构造”出的方案的偏差值(即和正确的构造方案多了多少数)
然后我们按照\(d_i\)降序的顺序进行“选择”
发现差值为\(d_i\)的一组元素如果交换位置关系,即\(a_{2i},a_{2i-1}\)交换
会对\(sm\)产生\(2d_i\)的影响,即\(sm-2d_i\),那么我们“选择”的过程就是消除原本的构造方案差值的过程
这样,我们就实现了对这道题的构造,并没有不合法的方案(如果你不想看证明了,现在就可以开始愉快的切题了)
我们开始大面积证明这样构造的正确性。。。。
首先,显然,加入一个元素\(0\)不会对答案产生影响
那么我们考虑为什么一定没有不合法方案
发现每次交换元素位置产生的总贡献一定是一个偶数,那么我们现在要证明的问题变为了\(\sum d_i\in even\),
start
我们构造的前提是数组长度为偶数,又有偶数个奇数,则一定有偶数个偶数
那么分两种情况讨论,
- 偶数个数大于奇数
这时我们排完序后的序列一部分肯定是奇数偶数交错的,这时两两分组一定会产生偶数对儿奇数偶数匹配
他们的差值是奇数,有偶数个差值,这一部分的\(\sum d\)为偶数,剩下的更好说,偶数偶数配对差值都是偶数
这种情况得证
- 偶数个数小于奇数
前面奇数偶数交错的部分跟前面一样,后面剩下的一定是偶数个奇数,他们匹配差值都是偶数,也得证
end
证明完\(\sum d\)为偶数后,发现证明的问题为差值从大到小进行消除一定有合法的解
不难发现如果所有的差值都被选择最终\(sm<0\)(因为\(sm\)是\(d\)加出来的,拿\(2d\)去减它一定会使他小于零)
而我们会想到有一种情况可能会无解,即当\(sm=2\)时最小的差值是\(2\)或者更大,这样减完就会是负的,\(2-2*2<0\)
那么我们现在要找一种让差值尽可能都\(\geq 2\)的数列,如果他也有解,那么所有的的数列都有解,所有的问题就都解决了
那么我们考虑把\([1.m]\)中的偶数都选上,这样会有\(\frac{m}{2}\)个偶数,那么剩下的\(\frac{2m}{3}-\frac{m}{2}\)个数只能是奇数
不妨把他们规定为\(1,3,5,...\),因为其他的跟这几个都一样的
我们发现有长度为\(2\times(\frac{2m}{3}-\frac{m}{2})\)的公差为一的等差数列
这样两两分组后差值为\(1\)的二元组就会有\(\frac{2m}{3}-\frac{m}{2}=\frac{m}{6}\)这么多个,剩下\(\frac{n}{2}-\frac{m}{6}\)组偶数保证其差值\(\geq 2\)
然后我们发现\(sm\leq m-\frac{n}{2}<n\)(唯独不知道这里是为什么,题解上说的就用吧)
\(\color{red}{馍馍zxs证明已经给到下面了}\)
确实不用考虑那个加一的问题,因为下面的不等式部分取值极限取不到(比如n>\(\frac{2m}{3}\),这样取到这个值最后的不等式符号是小于,加上\(1\)之后顶多是小于等于\(0\))
那么\(sm-4\times(\frac{n}{2}-\frac{m}{6})\leq m-\frac{n}{2}-2n+\frac{2m}{3}\leq \frac{5m}{3}-\frac{5n}{2}\leq \frac{5m}{3}-\frac{5}{2}\times \frac{2m}{3}< 0\)
所以不用差值为一的二元组就可以把\(sm\)消完,如果发现\(sm-2*d<0\)则可以找前面的差值为\(1\)的去消,那么以上的构造都是合理的
\(\color{red}{\huge{证完啦,手残啦!!!}}\)
不过我会很佩服能够从上面一直看到这里的人,我反正是没有这个耐心的(向你致敬~~)
chaoticevil
#include<bits/stdc++.h>
using namespace std;
const int NN=1e6+5;
namespace AE86{
FILE *wsn;
auto 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;
};
auto write=[](int x,char opt='\n'){
char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
};
}using namespace AE86;
int n,m,sm,c[NN];
vector<pair<int,int> > t;
struct node{int val,id;}a[NN];
namespace WSN{
inline short main(){
wsn=freopen("chaoticevil.in","r",stdin);
wsn=freopen("chaoticevil.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;++i)a[i]=node{read(),i};
if(n&1) a[++n]=node{0,n};
sort(a+1,a+n+1,[](node x,node y)->bool{return x.val<y.val;});
for(int i=1,d;i<=n;i+=2){
d=a[i+1].val-a[i].val;
t.push_back(make_pair(d,i)); sm+=d;
c[a[i+1].id]=1; c[a[i].id]=-1;
}
sort(t.begin(),t.end());
for(int i=t.size()-1,v,id;~i;--i){
v=t[i].first,id=t[i].second;
if(sm>=v*2)
sm-=v*2,swap(c[a[id].id],c[a[id+1].id]);
else if(sm==0){
puts("NP-Hard solved");if(!a[1].val)--n;
for(int j=1;j<=n;++j)printf("%d ",c[j]);
puts(""); return 0;
}
}
return 0;
}
}
signed main(){return WSN::main();}
T4 校门外歪脖树上的鸽子
正在改,拼命改
\(upd 2021.11.7\)
pigeons
#include<map>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int NN=4e5+5; namespace AE86{FILE *wsn;auto 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;};auto write=[](int x,char opt='\n'){char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);};}using namespace AE86;
int n,m,rng,ch[NN][2],fa[NN],root;
int ll[NN],rr[NN],len[NN],lst[NN],rst[NN];
map<int,int>mp[NN];
inline int pos(int x){return ch[fa[x]][1]==x;}
inline int bro(int x){return x==root?root:ch[fa[x]][pos(x)^1];}
inline void build(int x){
ll[x]=rr[x]=x;
if(ch[x][0])build(ch[x][0]),ll[x]=ll[ch[x][0]];
if(ch[x][1])build(ch[x][1]),rr[x]=rr[ch[x][1]];
if(ll[x]>rr[x]) swap(ch[x][0],ch[x][1]),ll[x]=ll[ch[x][0]],rr[x]=rr[ch[x][1]];
len[x]=rr[x]-ll[x]+1; mp[ll[x]][rr[x]]=x;
}
namespace tree_division{
int dfn[NN],rk[NN],dep[NN],siz[NN],son[NN],top[NN],cnt;
inline void dfs1(int f,int x){
dep[x]=dep[f]+1; siz[x]=1;
if(x==root) lst[x]=rst[x]=x;
else lst[x]=pos(x)?x:lst[fa[x]],rst[x]=pos(x)?rst[fa[x]]:x;
if(ch[x][0]) dfs1(x,ch[x][0]),siz[x]+=siz[ch[x][0]];
if(ch[x][1]) dfs1(x,ch[x][1]),siz[x]+=siz[ch[x][1]];
son[x]=(siz[ch[x][0]]>siz[ch[x][1]])?ch[x][0]:ch[x][1];
}
inline void dfs2(int x,int t){
top[x]=t; dfn[x]=++cnt; rk[cnt]=x;if(!son[x]) return; dfs2(son[x],t);
(son[x]==ch[x][0])?dfs2(ch[x][1],ch[x][1]):dfs2(ch[x][0],ch[x][0]);
}
inline int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);x=fa[top[x]];
}if(dfn[x]>dfn[y]) swap(x,y);
return x;
}
inline int findrt(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_division;
struct SNOWtree{
#define lid (id<<1)
#define rid (id<<1|1)
int ll[NN<<2],rr[NN<<2],w[NN<<2],sm[NN<<2],laz[NN<<2];
inline void pushup(int id){if(ll[id]!=rr[id])sm[id]=sm[lid]+sm[rid];}
inline void down(int id,int v){sm[id]+=v*w[id];laz[id]+=v;}
inline void pushdown(int id){if(ll[id]!=rr[id]&&laz[id])down(lid,laz[id]),down(rid,laz[id]),laz[id]=0;}
inline void build(int id,int l,int r,int opt){
ll[id]=l;rr[id]=r;if(l==r)return w[id]=(pos(bro(rk[l]))!=opt)?len[bro(rk[l])]:0,void();
int mid=(l+r)>>1; build(lid,l,mid,opt); build(rid,mid+1,r,opt); w[id]=w[lid]+w[rid];
}
inline void modify(int id,int l,int r,int v){
if(l<=ll[id]&&rr[id]<=r) return down(id,v),void(); pushdown(id);
int mid=ll[id]+rr[id]>>1;if(l<=mid)modify(lid,l,r,v);if(r>mid)modify(rid,l,r,v);pushup(id);
}
inline int query(int id,int l,int r){
if(l<=ll[id]&&rr[id]<=r)return sm[id];pushdown(id);int mid=ll[id]+rr[id]>>1,ans=0;
if(l<=mid) ans+=query(lid,l,r);if(r>mid) ans+=query(rid,l,r); return ans;
}
inline void update(int x,int y,int v){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
modify(1,dfn[top[x]],dfn[x],v);x=fa[top[x]];
}if(dfn[x]>dfn[y])swap(x,y);modify(1,dfn[x]+1,dfn[y],v);
}
inline int calc(int x,int y,int ans=0){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
ans+=query(1,dfn[top[x]],dfn[x]);x=fa[top[x]];
}if(dfn[x]>dfn[y])swap(x,y);return ans+query(1,dfn[x]+1,dfn[y]);
}
}tr[2];
inline void update(int x,int v){tr[pos(x)^1].modify(1,dfn[bro(x)],dfn[bro(x)],v);}
inline int query(int x){return tr[pos(x)^1].query(1,dfn[bro(x)],dfn[bro(x)]);}
int opt,x,y,d,lca;
auto solve=[](){
opt=read();x=read();y=read();lca=LCA(x,y);
// cout<<opt<<" "<<x<<" "<<y<<" "<<lca<<endl;
if(opt==1){
d=read();
if(mp[x].find(y)!=mp[x].end()) return update(mp[x][y],d),void();
int X=lst[x],Y=rst[y];
if(dep[X]<=dep[lca]) update(findrt(x,lca),d);
else update(X,d),tr[0].update(X,findrt(x,lca),d);
if(dep[Y]<=dep[lca]) update(findrt(y,lca),d);
else update(Y,d),tr[1].update(Y,findrt(y,lca),d);
}else{
if(mp[x].find(y)!=mp[x].end()) return write(query(mp[x][y])),void();
int X=lst[x],Y=rst[y],ans=0;
if(dep[X]<=dep[lca]) ans+=query(findrt(x,lca));
else ans+=query(X)+tr[0].calc(X,findrt(x,lca));
if(dep[Y]<=dep[lca]) ans+=query(findrt(y,lca));
else ans+=query(Y)+tr[1].calc(Y,findrt(y,lca));
write(ans); return;
}
};
namespace WSN{
inline short main(){
wsn=freopen("pigeons.in","r",stdin);
wsn=freopen("pigeons.out","w",stdout);
n=read();m=read();rng=2*n-1;
for(int i=n+1;i<=rng;i++){
ch[i][0]=read();ch[i][1]=read();
fa[ch[i][0]]=fa[ch[i][1]]=i;
} for(int i=n+1;i<=rng;i++)if(!fa[i]){root=i;break;}
build(root);dfs1(0,root);dfs2(root,root);
tr[0].build(1,1,rng,0);tr[1].build(1,1,rng,1);
while(m--)solve();
return 0;
}
}
signed main(){return WSN::main();}