NOI模拟8
沈老师如约切掉了两个题,在我啥也不会的时候eee%%%
T1考场上神奇的蒙了个构造方式就60pts了,T2看了一眼就是圆方树直接走人,T3打了暴力和部分分,一直局限在某一值域上
T1 出题人
发现可以找到一个环,然后找到一个结论,双向搜索
这里主要说一下归并的过程:
说是搜索,千万不要写搜索,因为那样的复杂度是假的,我们枚举下一个数放到哪个集合中,然后归并,这样的复杂度是对的
然而我写的时候并没有意识到这个,于是带着log,大力卡常...
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=14349000;
const int inf=1e12;
int n,m,a[35],b[35],ans1[35],ans2[35];
int pw[35],lm;
vector<int> s,t,o;
struct node{
int first,second;
node(){}node(int a,int b){first=a;second=b;}
bool operator < (node a)const{
return first<a.first;
}
bool operator == (node a)const{
return first==a.first;
}
};
vector<node> fi,se;
void dfs1(int x,int zt,int as){
if(x==m+1)return fi[zt]=node(as,zt),void();
dfs1(x+1,(zt<<1)+zt,as);
dfs1(x+1,(zt<<1)+zt+1,as+a[x]);
dfs1(x+1,(zt<<1)+zt+2,as-a[x]);
}
bool flag=false;
void dfs2(int x,int zt,int as){
if(flag)return ;
if(1.0*clock()/CLOCKS_PER_SEC>=1.95){printf("No");exit(0);}
if(x==n+1){
if(zt==0)return ;
node now=node(as,zt);
node ps=*lower_bound(fi.begin(),fi.end(),now);
if(now==ps){
fi.push_back(ps);se.push_back(now);
flag=true;
}
return ;
}
dfs2(x+1,(zt<<1)+zt,as);
dfs2(x+1,(zt<<1)+zt+1,as+a[x]);
dfs2(x+1,(zt<<1)+zt+2,as-a[x]);
}
signed main(){
freopen("problemsetter.in","r",stdin);
freopen("problemsetter.out","w",stdout);
n=read();m=n+1>>1;
bool even=false;int wo;
fo(i,1,n){
a[i]=read();
if(a[i]%2==0)even=true,wo=a[i];
}
if(even){
printf("Yes\n");
b[1]=wo/2;int now=1;
fo(i,1,n){
if(a[i]==wo)ans1[i]=ans2[i]=1;
else ans1[i]=now,ans2[i]=++now,b[now]=a[i]-b[now-1];
}
fo(i,1,n)printf("%lld ",b[i]);printf("\n");
fo(i,1,n)printf("%lld %lld\n",ans1[i],ans2[i]);
return 0;
}
fo(i,1,n)a[i]+=inf;
pw[0]=1;fo(i,1,n)pw[i]=pw[i-1]*3;
fi.resize(pw[m]);dfs1(1,0,0);
sort(fi.begin(),fi.end());
for(node i:fi)if(!i.first&&i.second){
flag=true;fi.push_back(i);se.push_back(node(0,0));break;
}
dfs2(m+1,0,0);
if(!flag){printf("No");return 0;}
cerr<<1.0*clock()/CLOCKS_PER_SEC<<endl;
int now=fi.back().second;
fu(i,m,1){
if(now%3==1)s.push_back(i);
else if(now%3==2)t.push_back(i);
else o.push_back(i);
now/=3;
}
now=se.back().second;
fu(i,n,m+1){
if(now%3==1)t.push_back(i);
else if(now%3==2)s.push_back(i);
else o.push_back(i);
now/=3;
}
b[1]=0;now=1;
while(s.size()){
now++;b[now]=a[s.back()]-inf-b[now-1];
ans1[s.back()]=now-1;ans2[s.back()]=now;
s.pop_back();
if(s.size()){
now++;b[now]=a[t.back()]-inf-b[now-1];
ans1[t.back()]=now-1;ans2[t.back()]=now;
t.pop_back();
}
}
ans1[t.back()]=1;ans2[t.back()]=now;
for(int i:o)b[++now]=a[i]-inf,ans1[i]=1,ans2[i]=now;
printf("Yes\n");
fo(i,1,n)printf("%lld ",b[i]);printf("\n");
fo(i,1,n)printf("%lld %lld\n",ans1[i],ans2[i]);
}
T2 彩色挂饰
这,圆方树题了,先缩了
设f[x][c]表示x点颜色是c的最小次数,这是圆点的定义
方点则表示他爹的颜色是c的最小次数
圆点可以直接转移,方点的话可以枚举相连的每个圆点的个数,再做一个小dp,转移就行了
这代码里的圆方树是错的...
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=2e5+5;
const int inf=0x3f3f3f3f;
int n,m,k,ss,co[N];
vector<int> d[N],e[N];
int dfn[N],low[N],cnt;
int sta[N],top;
bool fd[N];
void tarjan(int x){
dfn[x]=low[x]=++cnt;
sta[++top]=x;
for(int y:d[x]){
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]==dfn[x]){
n++;fd[n]=true;
while(sta[top]!=x){
e[sta[top]].push_back(n);
e[n].push_back(sta[top]);
top--;
}
e[x].push_back(n);e[n].push_back(x);
}
}
else low[x]=min(low[x],dfn[y]);
}
}
int dp[N][25],zy[1<<7][25],zg[1<<7],hh[1<<7];
int ys[7],cys,pp[7];
bool lt[1<<7];int in[N];
void dfs_dp(int x,int f){
fo(c,1,k)dp[x][c]=1;
for(int y:e[x])if(y!=f)dfs_dp(y,x);
if(fd[x]){
// cerr<<x<<endl;
cys=0;for(int y:e[x])ys[++cys]=y,in[y]=cys,pp[cys]=0;
for(int y:e[x]){
for(int z:d[y])if(in[z]){
pp[in[y]]|=1<<(in[z]-1);
}
}
int u=(1<<cys)-1;
fo(s,0,u)lt[s]=false;
fo(i,1,cys)lt[1<<i-1]=true;
fo(s,1,u){
if(lt[s]){
// cerr<<s<<" ";
fo(i,1,cys)if((!((s>>(i-1))&1))&&(s&pp[i])){
lt[s|(1<<i-1)]=true;
}
}
zg[s]=inf;
fo(c,1,k){
if(!lt[s]){
zy[s][c]=inf;
continue;
}
zy[s][c]=1;
fo(i,1,cys)if((s>>(i-1))&1){
zy[s][c]+=dp[ys[i]][c]-1;
}
zg[s]=min(zg[s],zy[s][c]);
}
}
// cerr<<endl;
hh[0]=0;fo(s,1,u)hh[s]=inf;
fo(s,0,u)for(int t=(u^s);t;t=(t-1)&(u^s)){
hh[s|t]=min(hh[s|t],hh[s]+zg[t]);
}
fo(c,1,k){
dp[x][c]=inf;
if(co[x]&&co[x]!=c)continue;
fo(s,0,u)if(((s>>in[f]-1)&1)){
dp[x][c]=min(dp[x][c],zy[s][c]+hh[u^s]);
}
}
for(int y:e[x])in[y]=0;
}
else {
fo(c,1,k){
if(co[x]&&co[x]!=c){
dp[x][c]=inf;
continue;
}
dp[x][c]=1;
for(int y:e[x])if(y!=f){
dp[x][c]+=dp[y][c]-1;
}
}
}
// cerr<<x<<" ";
// fo(c,1,k)cerr<<dp[x][c]<<" ";
// cerr<<endl;
}
signed main(){
freopen("colorful.in","r",stdin);
freopen("colorful.out","w",stdout);
n=read();m=read();k=read();ss=read();
fo(i,1,n)co[i]=read();
fo(i,1,m){
int x=read(),y=read();
d[x].push_back(y);d[y].push_back(x);
}
int nn=n;fo(i,1,nn)if(!dfn[i])tarjan(i);
// cerr<<n<<endl;
dfs_dp(1,0);
int ans=inf;
fo(c,1,k)ans=min(ans,dp[1][c]);
printf("%lld\n",ans);
return 0;
}
T3 逆转函数
说实话,这就是一个互相对应的回文串,所以跑马拉车
千万不要想着去记录函数的对应,而是记录位置的对应,这样的话就可以很方便的翻转过来了...
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=6e5+5;
const int mod=998244353;
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
int n,m,a[N],pm[N],ans;
int nxt[N],pre[N],pos[N];
int p[N],as[N],sm[N];
signed main(){
freopen("invfunc.in","r",stdin);
freopen("invfunc.out","w",stdout);
n=read();m=read();
pm[0]=1;fo(i,1,m)pm[i]=pm[i-1]*m%mod;
fo(i,1,n)a[i]=read();
fu(i,n,1)a[i*2]=a[i],a[i*2+1]=0;a[1]=0;
fo(i,0,m)pos[i]=0;
fo(i,1,2*n+1){
pre[i]=pos[a[i]];
pos[a[i]]=i;
}
fo(i,0,m)pos[i]=2*n+2;
fu(i,2*n+1,1){
nxt[i]=pos[a[i]];
pos[a[i]]=i;
}
// fo(i,1,2*n+1){
// cerr<<a[i]<<" "<<pre[i]<<" "<<nxt[i]<<endl;
// }
int mx=0,id;
fo(i,1,2*n){
if(i<mx){
int ls=id*2-i;
p[i]=min(p[ls],mx-i);
sm[i]=sm[ls];as[i]=as[ls];
fu(j,p[ls],p[i]+1){
int l=ls-j+1,r=ls+j-1;
if(a[l]){
sm[i]-=pm[as[i]];
as[i]+=(nxt[l]>r)+(pre[r]<=l);
}
}
}
else {
// cerr<<i<<endl;
p[i]=1;
if(a[i]){
sm[i]=pm[m-1];
as[i]=m-1;
}
else sm[i]=0,as[i]=m;
}
int l=i-p[i],r=i+p[i];
while(l>=1&&r<=2*n+1&&(nxt[l]>=r||a[r]==a[l+r-nxt[l]])&&(pre[r]<=l||a[l]==a[l+r-pre[r]])){
p[i]++;
if(a[l]){
as[i]-=(nxt[l]>r)+(pre[r]<=l);
sm[i]+=pm[as[i]];
}
l=i-p[i];r=i+p[i];
}
if(i+p[i]>mx)mx=i+p[i],id=i;
// cerr<<i<<" "<<p[i]<<" "<<sm[i]<<" "<<as[i]<<endl;
}
fo(i,1,2*n+1)ans=(ans+sm[i])%mod;
printf("%lld\n",ans);
}
QQ:2953174821