Loading

Noip模拟86 2021.10.30

别人都觉得这一场的题写着没意思,然而自己啥都不会,好菜啊。。。

今天这一套题确实没有什么思路(逃)

T1 特殊字符串

就是个纯\(dp\),但是考场上只想到了\(O(n^3)\)的,并不太懂为什么去掉了一维限制转移没有问题

然后就可以优化到\(O(26n)\)的复杂度了

当时还想到了一种\(fail\)树上\(dp\),可是码完调不出来就弃掉了

总之我的\(dp\)设的是\(f_{i,j,k}\)表示考虑到长串的第\(i\)位,子串选择的长度为\(j\),上一次选择的位置在\(k\)的答案

然后哈希加\(map\)判断匹配,确实比较麻烦

RE40
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=1e5+5;
namespace AE86{
	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;
typedef unsigned long long ULL;
const ULL base=131;
int n,m,f[105][105][105];
char s[NN];
unordered_map<ULL,int> mp;
struct node{char s1[2],s2[2];int k;}p[NN];
namespace WSN{
	inline short main(){
		// freopen("in.in","r",stdin);
		freopen("shiki.in","r",stdin);
		freopen("shiki.out","w",stdout);
		n=read(); scanf("%s",s+1); m=read();
		for(int i=1;i<=m;i++){
			ULL has=0;char ch;
			scanf("%s",p[i].s1);
			scanf("%s",p[i].s2);
			p[i].k=read();
			has=has*base+(p[i].s1[0]-'a');
			has=has*base+(p[i].s2[0]-'a');
			if(mp.find(has)==mp.end()) mp[has]=i;
			else{
				p[mp[has]].k+=p[i].k;
			}
		}
		for(int i=2;i<=n;i++){
			for(int j=2;j<=i;j++){
				for(int k=1;k<i;k++){
					ULL has=0; int tmp=0;
					has=has*base+(s[k]-'a');has=has*base+(s[i]-'a');
					f[i][j][k]=max(f[i][j][k],f[i-1][j][k]);
					if(mp.find(has)!=mp.end()) tmp=p[mp[has]].k;
					f[i][j][i]=max(f[i][j][i],f[i-1][j-1][k]+tmp);
				}
			}
		}
		int ans=0;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				ans=max(ans,f[n][i][j]);
			}
		}
		printf("%lld\n",ans);
		return 0;
	}
}
signed main(){return WSN::main();}

题解的\(dp\)就比较人性化了,\(f_{i,j}\)表示考虑到长串的第\(i\)位,上一次选择的字符是\(j\)的答案

这样的话状态可能会计算到不合法的,那么需要赋初值,

于是考场上以为这种方法会算多,于是没有敢直接枚举\(26\)个字符,而是按照上一次的位置判断转移了

至于为什么不用加一维子串的长度,感性理解一下可能不加确实也对。。。。

于是就可以瞎转移了

\(f_{i,j}=f_{i-1,j}(s_i!=j)\)

\(f_{i,j}=\min(f_{i-1,k}+a_{k,j})(s_i=j)\)

其中\(a_{k,j}\)是字符二元组产生的贡献

shiki
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=1e5+5;
namespace AE86{
	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[27][27],dp[NN][27];
char s[NN],s1[3],s2[3];
namespace WSN{
	inline short main(){
		freopen("shiki.in","r",stdin);
		freopen("shiki.out","w",stdout);
		n=read(); scanf("%s",s+1); m=read();
		for(int i=1;i<=m;i++){
			scanf("%s",s1);scanf("%s",s2);
			a[s1[0]-'a'][s2[0]-'a']+=read();
		}
		memset(dp,-0x3f,sizeof(dp));
		dp[1][s[1]-'a']=0;
		for(int i=2;i<=n;i++){
			for(int j=0;j<26;j++){
				if(dp[i-1][j]>=0) dp[i][j]=dp[i-1][j];
				if(s[i]-'a'==j){
					for(int k=0;k<26;k++)if(dp[i-1][k]>=0)
						dp[i][j]=max(dp[i][j],dp[i-1][k]+a[k][j]);
				}
			}
		}
		write(dp[n][s[n]-'a']);
		return 0;
	}
}
signed main(){return WSN::main();}

T2 宝可梦

考场上打了个模拟就跑了

发现从一个度数为\(1\)的点开始走一圈,一定能把所有地方都走到(度数为\(1\)表示这个点只能从一个方向出发)

那么只模拟一次走的过程,把这个“环”找出来,一定包含了所有点的不同转移状态在“环”上的编号

其中状态表示一个三元组\((i,j,k)\)表示这个点的坐标\((i,j)\)以及从哪一个方向\(k=0/1/2/3\)转移来的

那么对于每个询问,只需要用这些状态在“环”上的位置的编号来计算距离即可

每组询问给定一个\((sx,sy),(tx,ty),dir\),发现初始状态并不能使用\((sx,sy,dir)\),而是应该使用起点走出去的那个点\(now\)为初始状态

即以\((now.x,now.y,dir)\)为初始状态,而我们并不知道终止节点\((tx,ty)\)是从哪一个方向转移来的,那么我们直接枚举四个方向,找到最小的编号差即可

注意出负数之后加上一个“环”长,以及判断起点终点是否相等

pokemon
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=1e6+5;
namespace AE86{
	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,q,cir[NN][4];
bool g[NN],to[NN][4];
char ch[NN],s[3];
int dx[4]={-1,1,0,0},
	dy[4]={0,0,-1,1};//上0下1左2右3
struct node{int x,y;bool operator!=(const node&a)const{return x!=a.x||y!=a.y;}};
inline int id(int x,int y){return x*(m+2)+y;}
inline int getd(char s){if(s=='U')return 0;else if(s=='D')return 1;else if(s=='L')return 2;else return 3;}
inline int rit(int d){if(d==3) return 1;else if(d==0) return 3;else if(d==1) return 2;else return 0;}
inline int lef(int d){if(d==2) return 1;else if(d==0) return 2;else if(d==3) return 0;else return 3;}
node S,now;
int tot,x,y,l,k,r;
auto findcircle=[](){
	for(int i=1;i<=n;i++){for(int j=1;j<=m;j++)
		if(g[id(i,j)]&&to[id(i,j)][0]+to[id(i,j)][1]+to[id(i,j)][2]+to[id(i,j)][3]==1){now=S=node{i,j};break;}
		if(S.x) break;
	}
	for(int i=0;i<4;i++)if(to[id(now.x,now.y)][i]){now=node{now.x+dx[i],now.y+dy[i]},k=i;break;}
	cir[id(now.x,now.y)][k]=++tot;
	while(now!=S){
		x=now.x,y=now.y,r=rit(k);
		if(g[id(x+dx[r],y+dy[r])]){
			k=r,now=node{x+dx[r],y+dy[r]};
		}
		else if(!g[id(x+dx[k],y+dy[k])]){
			l=k;while(!g[id(x+dx[l],y+dy[l])]) l=lef(l);
			k=l,now=node{x+dx[l],y+dy[l]};
		}
		else now=node{x+dx[k],y+dy[k]};
		cir[id(now.x,now.y)][k]=++tot;
	}
};
int sx,sy,tx,ty,ans;
auto solve=[](){
	sx=read(),sy=read(),tx=read(),ty=read(),ans=0x3fffffff;scanf("%s",s+1);
	if(sx==tx&&sy==ty) return puts("0"),void();
	k=getd(s[1]);
	int tmp=cir[id(sx+dx[k],sy+dy[k])][k],res,fck;
	for(int i=0;i<4;i++)if(cir[id(tx,ty)][i]){
		res=cir[id(tx,ty)][i];
		fck=res-tmp+1;
		if(fck<0) fck+=tot;
		ans=min(ans,fck);
	}
	write(ans);return;
};
namespace WSN{
	inline short main(){ freopen("pokemon.in","r",stdin);freopen("pokemon.out","w",stdout);
		n=read(); m=read();
		for(int i=1;i<=n;i++){scanf("%s",ch+1);for(int j=1;j<=m;j++)g[id(i,j)]=(ch[j]=='.');}
		for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
			for(int k=0;k<4;k++)if(g[id(i+dx[k],j+dy[k])])to[id(i,j)][k]=1;
		findcircle();
		q=read();
		while(q--)solve();
		return 0;
	}
}
signed main(){return WSN::main();}

T3 矩阵

败笔,考场上因为认为复杂度不对而且不一定正确而没有打爆搜

不说了,说多了都是泪。。。

判断无解只需要看是否有两个挨着的点相等就行(显然)

考虑如何拼出最长的等比数列

枚举一个点\((i,j)\)以及他的四个方向\(0,1,2,3\),找到他们之间的公比(如果有)

然后将所有的点按照权值大小排序,这样保证最长的等比数列一定是排完序后的数列的子序列

然后找每一种状态\((i,j,k=0/1/2/3)\)是否和一个相邻的状态公比相等,累加长度即可

其中状态相邻就表示两个点的坐标相邻,并且方向相反

matrix
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=40010;
namespace AE86{
	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,w[NN],rng,d[NN][5],cnt[NN][5];
struct node{
	int x,y,w;
	bool operator<(const node&a)const{
		return w<a.w;
	}
}p[NN];
inline int id(int x,int y){return (x-1)*m+y;}
inline int fd(int x,int y,int opt){
	if(opt==0) return id(x+1,y);
	if(opt==1) return id(x-1,y);
	if(opt==2) return id(x,y+1);
	if(opt==3) return id(x,y-1);
	return 0;
}
namespace WSN{
	inline short main(){
		freopen("matrix.in","r",stdin);
		freopen("matrix.out","w",stdout);
		n=read(); m=read();
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++){
				w[id(i,j)]=read(),p[++rng]=node{i,j,w[id(i,j)]};
			}
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++){
				if(i>1) if(w[id(i,j)]==w[id(i-1,j)]) return puts("-1"),0;
				if(j>1) if(w[id(i,j)]==w[id(i,j-1)]) return puts("-1"),0;
			}
		for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=0;k<4;k++) cnt[id(i,j)][k]=1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++){
				if((i<n)&&(w[id(i,j)]%w[id(i+1,j)]==0)) cnt[id(i,j)][0]=2,d[id(i,j)][0]=w[id(i,j)]/w[id(i+1,j)];
				if((i>1)&&(w[id(i,j)]%w[id(i-1,j)]==0)) cnt[id(i,j)][1]=2,d[id(i,j)][1]=w[id(i,j)]/w[id(i-1,j)];
				if((j<m)&&(w[id(i,j)]%w[id(i,j+1)]==0)) cnt[id(i,j)][2]=2,d[id(i,j)][2]=w[id(i,j)]/w[id(i,j+1)];
				if((j>1)&&(w[id(i,j)]%w[id(i,j-1)]==0)) cnt[id(i,j)][3]=2,d[id(i,j)][3]=w[id(i,j)]/w[id(i,j-1)];
			}
		sort(p+1,p+rng+1);
		for(int i=1;i<=rng;i++)
			for(int j=0;j<4;j++){
				int x=p[i].x,y=p[i].y,pos=id(x,y);
				if(!d[pos][j]) continue;
				for(int k=0;k<4;k++){
					if(x==n&&k==0) continue;
					if(x==1&&k==1) continue;
					if(y==m&&k==2) continue;
					if(y==1&&k==3) continue;
					int to=fd(x,y,k);
					if(d[to][k^1]!=d[pos][j]) continue;
					cnt[to][k^1]=max(cnt[to][k^1],cnt[pos][j]+1);
				}
			}
		int ans=1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				for(int k=0;k<4;k++)
					ans=max(ans,cnt[id(i,j)][k]);
		write(ans);
		return 0;
	}
}
signed main(){return WSN::main();}

T4 乘法

头一次见这么长的文件名\(multiplication\),打着有够爽

保留十六位,可以直接用\(\textit{unsigned long long}\)自然溢出

去掉末尾的\(0\)保留最后的\(16\)位,如果这个数转化成二进制其末尾的\(0\)的个数不会超过\(3\)个,否则满\(16\)位进\(1\)原数后面又多了一个\(0\)

这样我们可以算出\(n!\)中有多少个\(2\),那么把它们全部去掉,最后每四个\(2\)进一位,把进位的数进上就可以了,具体操作就是\(ans+=cnt2\mod 4\)

那么剩下的数全部是奇数,问题就变为了求\(\prod\limits_{i=1}^{64} f_{\frac{n}{2^i}}\),其中\(f_{n}=\prod\limits_{i=0}^{\frac{n-1}{2}}(2i+1)\)

问题在于如何求\(f_n\),令\(up=\frac{n-1}{2}\)

由于我们只会保留前面的\(64\)位,我们可以将它看成一个多项式,令\(x=2\),取模\(x^{64}\),则有

\(F(x)=\prod\limits_{i=0}^{up}(2i+1)(\mod 64)\)

\(f=\prod\limits_{i=0}^{63}2^iF[x^i]\)

当时比较傻不懂为啥是到\(63\),到处询问最后发现是对\(2^{64}\)取模,然后就死掉了

\(g(x,i)=F[x^i]\)

现在问题在于如何求出这个\(g\),即多项式系数

考虑其组合意义\(F[x^i]\)相当于在\([1,up]\)中选择\(i\)个数,不同的方案的乘积加和

没有重复元素的无序序列的乘积,这就可以背包求出,考虑在前面的无序序列的最后加一个数,这数可能与前面的数重复,就需要容斥,枚举重复的元素个数,

那么就有\(g(x,i)=\sum\limits_{j=1}^{i}(-1)^{j-1}g(x,i-j)\sum\limits_{y\in [1,x]}y^j\)

后面的\(\sum\limits_{y\in [1,x]}y^j\),可以用斯特林反演求出,比较高级,大体如下

\(\sum_{x=0}^{T} x^{i}=\sum_{x=0}^{T} \sum_{k=0}^{i}\begin{Bmatrix}i\\k\end{Bmatrix}x^{\underline{k}}=\sum_{k=0}^{i}\begin{Bmatrix}i\\k\end{Bmatrix}\sum_{x=0}^{T} x^{k}=\sum_{k=0}^{i}\begin{Bmatrix}i\\k\end{Bmatrix} k ! \sum_{x=0}^{T}\begin{pmatrix}x\\k\end{pmatrix}=\sum_{k=0}^{i}\begin{Bmatrix}i\\k\end{Bmatrix}k !\begin{pmatrix}T+1\\k+1\end{pmatrix}\)

那么把后面的一部分预处理就可以做到\(log^2\)或者\(log^3\)复杂度了

multiplication
#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
namespace AE86{
	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;
const int NN=65,o=1;
int T,n,h[NN],S[NN][NN],v[NN],f[NN],g[NN],cnt2[NN];
inline int qmo(int a,int b,int ans=o){
	for(;b;b>>=o,a=a*a)if(b&o)ans=ans*a;
	return ans;
}
auto prework=[](){
	h[0]=h[1]=1; S[0][0]=1;
	for(int i=2;i<NN;i++)h[i]=h[i-1]*i;
	for(int i=1;i<NN;i++)for(int j=1;j<=i;j++)
		S[i][j]=S[i-1][j-1]+S[i-1][j]*j;
	for(int i=1;i<=64;i++){
		cnt2[i]=0; int tmp=i;
		while(!(tmp&o))++cnt2[i],tmp>>=o;
		if(cnt2[i]) v[i]=v[i>>cnt2[i]];
		else v[i]=qmo(tmp,(o<<63)-o);
	}
};
inline int sp(int x,int i){return (x>>cnt2[i])*v[i];}
inline int calc(int x){
	if(!x)return o;
	int up=(x-o)>>o,res=0;
	for(int i=0;i<=min((int)63,up);i++)g[i]=0;
	for(int i=0;i<=min((int)63,up);i++){
		f[i]=0; int tmp=up+o;
		for(int j=1;j<=i;j++){
			tmp=tmp*(up+o-j);
			tmp=sp(tmp,j+o);
			f[i]+=tmp*h[j]*S[i][j];
		}
	}
	g[0]=1;
	for(int i=1;i<=min((int)63,up);i++){
		for(int j=1,bs=1;j<=i;j++,bs*=-1)
			g[i]+=bs*g[i-j]*f[j];
		g[i]=sp(g[i],i);
	}
	for(int i=0;i<=min((int)63,up);i++)res+=(g[i]<<i);
	return res;
}
auto solve=[](){
	n=read();int ans=1,sum=0;
	for(int i=0;i<64;i++) ans*=calc(n>>i);
	for(int i=1;i<64;i++) sum+=(n>>i);
	sum&=(int)3; ans<<=sum;
	printf("%llX\n",ans);
};
namespace WSN{
	inline short main(){
		freopen("multiplication.in","r",stdin);
		freopen("multiplication.out","w",stdout);
		T=read();prework();
		while(T--)solve();
		return 0;
	}
}
signed main(){return WSN::main();}
posted @ 2021-10-31 08:47  雪域亡魂  阅读(103)  评论(0编辑  收藏  举报