zoukankan      html  css  js  c++  java
  • 2021.05.04【NOIP提高B组】模拟 总结

    T1

    题目大意, (S_{i,j}=sum_{k=i}^j a_k) ,求 (ans=min{ S_{i,j}mod P|S_{i,j}mod Pge K })

    其中 (ile j)({ S_{i,j}mod P|S_{i,j}mod Pge K }) 非空

    (s_i) 是取模的前缀和,显然是要满足条件的 ((s_j-s_i)mod P) 最小

    对于一个 (s_j) ,要 (s_i) 最大,变成一个查询前驱的问题

    但是有限制,考虑分类讨论

    1. (s_jge s_i) 时原式变为 (s_j-s_i) ,要满足要求, (s_i) 最大是 (s_j-K),直接查询 (s_j-K) 的前驱

      原因:(s_j-(s_j-K)=Kge K)

    2. (s_j<s_i) 时原式变为 (s_j-s_i+P) ,要满足要求, (s_i) 最大是 (s_j-K+P),查询 (s_j-K+P) 的前驱

    查询完,将 (s_i) 插入

    至于查询前驱,和普通的平衡树有一点区别:不严格前驱

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100005,INF=100000000;
    int n,K,P,ans=INF,s[N];
    int cnt[N],val[N],sz[N],fa[N],ch[N][2],root,tot;
    inline int Get(int x) { return ch[fa[x]][1]==x; }
    inline void Up(int x) { sz[x]=cnt[x]+sz[ch[x][0]]+sz[ch[x][1]]; }
    inline void Rot(int x) {
    	register int y=fa[x],z=fa[y],k=Get(x);
    	ch[z][Get(y)]=x,fa[x]=z;
    	ch[y][k]=ch[x][k^1],fa[ch[x][k^1]]=y;
    	ch[x][k^1]=y,fa[y]=x;
    	Up(y),Up(x);
    }
    inline void Splay(int x,int goal=0) {
    	for(int y;(y=fa[x])^goal;Rot(x))
    	if(fa[y]^goal)Rot(Get(x)^Get(y)?x:y);
    	if(!goal)root=x;
    }
    inline void Find(int x) {
    	if(!root)return;
    	register int u=root;
    	while(ch[u][x>val[u]] && x^val[u])
    		u=ch[u][x>val[u]];
    	Splay(u);
    }
    inline void Ins(int x) {
    	register int u=root,fu=0;
    	while(u && val[u]^x)fu=u,u=ch[u][x>val[u]];
    	if(u)cnt[u]++;
    	else {
    		u=++tot;
    		if(fu)ch[fu][x>val[fu]]=u;
    		ch[u][0]=ch[u][1]=0;
    		fa[u]=fu,val[u]=x;
    		cnt[u]=sz[u]=1;
    	}
    	Splay(u);
    }
    inline int Pre(int x) {
    	register int u=root,mx=-INF;
    	while(1) {
    		if(!u)return mx;
    		if(val[u]>x)u=ch[u][0];
    		else mx=max(mx,val[u]),u=ch[u][1];
    	}
    }
    int main() {
    	scanf("%d%d%d",&n,&K,&P);
    	Ins(0);
    	for(int i=1;i<=n;i++)
    		scanf("%d",s+i),
    		(s[i]+=s[i-1])%=P;
    	for(int i=1,xx,yy;i<=n;i++) {
    		xx=Pre(s[i]-K);
    		yy=Pre(s[i]-K+P);
    //		printf("%d %d
    ",xx,yy);
    		ans=min(ans,min(s[i]-xx,s[i]-yy+P));
    		Ins(s[i]);
    	}
    	printf("%d",ans);
    }
    

    T2

    (n)(D) 维的点,两点 (A(x_1,x_2,cdots,x_D),B(X_1,X_2,cdots,X_D)) 曼哈顿距离是 (sum_{i=1}^D |x_i-X_i|)

    问两点间的最大距离

    60

    由于 (D=2) ,将点放入平面直角坐标系,求的是 (max{ |x_i-x_j|+|y_i-y_j|})

    取绝对值,又四种情况

    (+x_i-x_j+y_i-y_j)

    (+x_i-x_j-y_i+y_j)

    (-x_i+x_j+y_i-y_j)

    (-x_i+x_j-y_i+y_j)

    再合并一下

    ((+x_i+y_i)+(-x_j-y_j))

    ((+x_i-y_i)+(-x_j+y_j))

    ((-x_i+y_i)+(+x_j-y_j))

    ((-x_i-y_i)+(+x_j+y_j))

    发现答案就在之中,

    于是分类讨论两个括号,相加取 (max) 即可

    100

    将上面算法改一下即可:将分类讨论变成 (O(2^D)) 的全排列

    排列同一个点的符号,然后记录对于所有点每一种排列的最大、最小值

    最后最大-最小取最大即可


    还可以更快的优化:

    因为 (x_i-x_j=-x_i-(-x_j))

    所以没有必要枚举第一个系数

    直接定为 1

    总复杂度 (O(nD imes 2^{D-1}))


    #include<bits/stdc++.h>
    using namespace std;
    const int N=1000005,INF=100000000;
    int n,D,up,tot,x[6],mx[40],mn[40],ans;
    void dfs(int st,int v) {
    	if(st>D) {
    		tot++;
    		mx[tot]=max(mx[tot],v);
    		mn[tot]=min(mn[tot],v);
    		return;
    	}
    	dfs(st+1,v+x[st]);
    	dfs(st+1,v-x[st]);
    }
    int main() {
    	scanf("%d%d",&n,&D);
    	up=1<<D;
    	for(int i=1;i<=up;i++)
    		mx[i]=-INF,mn[i]=+INF;
    	n=0;
    	while(scanf("%d",&x[1])!=EOF) {
    		for(int j=2;j<=D;j++)
    			scanf("%d",&x[j]);
    		tot=0,dfs(2,x[1]);
    	}
    	for(int i=1;i<=up;i++)
    		ans=max(ans,mx[i]-mn[i]);
    	printf("%d",ans);
    }
    

    T3

    (dfrac{1}{x}+dfrac{1}{y}=dfrac{1}{n}) 的本质不同的解的个数

    [egin{aligned} dfrac{1}{x}+dfrac{1}{y} &= dfrac{1}{n} \ nx+ny &= xy \ n^2 &= n^2-n(x+y)+xy \ (x-n)(y-n) &= n^2 end{aligned} ]

    所以,只要 ((x-n))((y-n))(n^2) 的约数,就是一个解

    于是分解质因数求 (n^2) 因数个数,注意

    1. 求的是 (n^2) 得因数个数,对于 (n) 得一个质因数次数要乘二
    2. 如果到最后 (n>1) 说明还有一个质因数,答案乘 3
    3. 要求本质不同的解,即 (lceil dfrac{ans}{2} ceil)
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    LL n,ans=1,cnt;
    int main() {
    	scanf("%lld",&n);
    	for(LL i=2;i<=sqrt(n);i++) {
    		if(n%i==0) {
    			cnt=0;
    			while(n%i==0)
    				n/=i,++cnt;
    			ans*=cnt<<1|1;
    		}
    	}
    	if(n>1)ans*=3;
    	printf("%lld",ans+1>>1);
    }
    

    T4

    题目大意: 对 (n imes m) 得地图染色,要求每一个 (2 imes 2) 得小方阵内颜色不能相同

    问方案数模 (P)(nle 10^{100},mle 5)

    (mle 5),显然的状压 dp

    50

    (f_{i,s}) 为前 (i-1) 行放满,第 (i) 行状态为 (s) 得方案

    (f_{i,s}=sum f_{i-1,t}) ,其中 (s,t) 为一组合法状态

    复杂度 (O(n imes 2^m imes 2^m)) 期望 50

    100

    然后转移是固定的,设 (g_{s,t}=1),其中 (s,t) 为一组合法状态

    (f_i) 变成一个 (2^m imes2^m) 的矩阵

    可得 (f_{i}=f{i-1} imes g)

    (f(n)=f(1) imes g^{n-1})

    (f(1)) 就第一行是 1,其余是 0

    所以用矩阵快速幂,最后输出矩阵中所有元素和

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    int len,tn,m,P,up,p[20],sum,n[200],ck[40][40];
    char s[200];
    inline bool chk(int S,int T) {
    	for(int i=0;i<m-1;i++) {
    		if(!(S&p[i]) && !(S&p[i+1]) && !(T&p[i]) && !(T&p[i+1]))return 0;
    		if((S&p[i]) && (S&p[i+1]) && (T&p[i]) && (T&p[i+1]))return 0;
    	}
    	return 1;
    }
    struct mat {
    	int a[40][40];
    	mat() { memset(a,0,sizeof(a)); }
    	inline mat operator*(mat x) {
    		register mat res;
    		for(int i=0;i<=up;i++)
    			for(int j=0;j<=up;j++)
    				for(int k=0;k<=up;k++)
    					(res.a[i][j]+=a[i][k]*x.a[k][j])%=P;
    		return res;
    	}
    }ans,tmp;
    int main() {
    	for(int i=0;i<=10;i++)p[i]=1<<i;
    	scanf("%s%d%lld",s+1,&m,&P);
    	len=strlen(s+1),up=(1<<m)-1;
    	for(int i=1;i<=len;i++)n[i]=s[len-i+1]^48;
    	n[1]--;
    	for(int i=1;i<=len;i++) {
    		if(n[i])break;
    		n[i]+=10,--n[i+1]; 
    	}
    	while(!n[len] && len)--len;
    	for(int j=0;j<=up;j++)
    		for(int k=0;k<=up;k++)
    			tmp.a[j][k]=chk(j,k);
    	for(int i=0;i<=up;i++)ans.a[1][i]=1; 
    	while(len) {
    		if(n[1]&1)ans=ans*tmp;
    		tmp=tmp*tmp;
    		for(int i=len;i>1;i--)
    			n[i-1]+=10*(n[i]&1),n[i]>>=1;
    		n[1]>>=1;
    		while(!n[len] && len)--len;
    	}
    	for(int i=0;i<=up;i++)
    		for(int j=0;j<=up;j++)
    			(sum+=ans.a[i][j])%=P;
    	printf("%d",sum);
    }
    

    总结

    1. 对于取模的分类讨论
    2. 对于绝对值的分类讨论
    3. 因式分解
    4. 转移固定就用矩阵乘
  • 相关阅读:
    perl教程
    信号量(semaphore)——POSIX信号量和System V信号量
    (C#习题) 杂题1
    (C#) 操作XML之遍历
    (C# 基础) Solution and Project
    (C#)枚举 Enumerations
    (WPF) 窗口间传参数
    (C#) 操作XML之查找
    (C#习题) 字符串
    (C#) VS类视图和对象浏览器图标
  • 原文地址:https://www.cnblogs.com/KonjakLAF/p/14730415.html
Copyright © 2011-2022 走看看