• 2021.03.06【NOIP提高B组】模拟 总结


    T1

    看起来十分复杂,打表后发现答案是 \(n*m\mod p\)

    具体的证明。。。

    原式的物理意义,就是从坐标原点(0,0),用每一种合法的斜率,

    穿过坐标[1 ~ n , 1 ~ m]的方阵中的整点的个数,总数即 n*m。

    T2

    考试时没想到 \(O(n^2)\) 的做法

    \[\begin{aligned} A_{k+i-1}-B_i &=A_{k+j-1}-B_j \\ A_{k+i-1}-A_{k+j-1} &= B_i-B_j \\ A_i-A_j &= B_i-B_j \end{aligned} \]

    所以只要先差分,再跑一边 \(\text{KMP}\) 或字符串哈希即可

    字符串哈希

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=1000005;
    const LL M1=998244353,M2=1004535809,B=233;
    int n,m,ans;
    LL X1[N],X2[N],P1[N],P2[N],Y1,Y2,x[N],y[N];
    inline bool check(int i) {
    	return
    	(Y1+1LL*P1[m]*X1[i-1]%M1)%M1==X1[i+m-1] &&
    	(Y2+1LL*P2[m]*X2[i-1]%M2)%M2==X2[i+m-1];
    }
    int main() {
    	freopen("sequence.in","r",stdin);
    	freopen("sequence.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%lld",&x[i]);
    	for(int i=1;i<=m;i++)scanf("%lld",&y[i]);
    	for(int i=1;i<n;i++)x[i]=x[i+1]-x[i],x[i]+=2e9;
    	for(int i=1;i<m;i++)y[i]=y[i+1]-y[i],y[i]+=2e9;
    	--n,--m;
    	P1[0]=P2[0]=1;
    	for(int i=1;i<=n;i++) {
    		X1[i]=(1LL*X1[i-1]*B%M1+1LL*x[i])%M1;
    		X2[i]=(1LL*X2[i-1]*B%M2+1LL*x[i])%M2;
    		P1[i]=(1LL*P1[i-1]*B)%M1;
    		P2[i]=(1LL*P2[i-1]*B)%M2;
    	}
    	for(int i=1;i<=m;i++) {
    		Y1=(1LL*Y1*B%M1+1LL*y[i])%M1;
    		Y2=(1LL*Y2*B%M2+1LL*y[i])%M2;
    	}
    	for(int i=1;i<=n-m+1;i++)
    		if(check(i))++ans;
    	printf("%d",ans);
    }
    

    \(\text{KMP}\)

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1000005;
    int n,m,ans,x[N],y[N],nxt[N];
    int main() {
    	freopen("sequence.in","r",stdin);
    	freopen("sequence.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%d",&x[i]);
    	for(int i=1;i<=m;i++)scanf("%d",&y[i]);
    	for(int i=1;i<n;i++)x[i]=x[i+1]-x[i];
    	for(int i=1;i<m;i++)y[i]=y[i+1]-y[i];
    	--n,--m;
    	for(int i=2,j=0;i<=m;i++) {
    		while(j && y[i]!=y[j+1])j=nxt[j];
    		if(y[i]==y[j+1])++j;
    		nxt[i]=j;
    	}
    	for(int i=0,j=0;i<=n;i++) {
    		while(j && x[i]!=y[j+1])j=nxt[j];
    		if(x[i]==y[j+1])++j;
    		if(j==m)++ans,j=nxt[j];
    	}
    	printf("%d",ans);
    }
    

    T3

    考试时:不考虑 \(k\) 直接树形 DP ,奇妙地拿了 30

    赛后:由于原题意思就是不能直接地祖孙关系,因为一个子树地 DFS 序是连续的

    只要选的区间不相交即可,用 DP 解决

    \(dp_{i,j}\) 为第 \(j\) 个果子选了 DFS 序为 \(i\) 的节点的最大美味值

    可以从 \(dp_{i+1,j}\)\(dp_{\text{next subtree},j}\) ,和 \(dp_{\text{next subtree},j-1}+w_{\text{node of i}}\) 转移

    数据十分奇妙,要开滚动数组。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100005;
    int n,k,op,dp[N][2],f[N],w[N],to[N],lst[N],nxt[N],rk[N],sz[N],odd,ans;
    void Pre(int u) {
    	rk[++odd]=u,sz[u]=1;
    	for(int i=lst[u],v;i;i=nxt[i])
    		Pre(v=to[i]),sz[u]+=sz[v];
    }
    int main() {
    	freopen("galo.in","r",stdin);
    	freopen("galo.out","w",stdout);
    	scanf("%d%d",&n,&k),++k;
    	for(int i=2;i<=n;i++) {
    		scanf("%d%d",&f[i],&w[i]);
    		to[i]=i,nxt[i]=lst[f[i]];
    		lst[f[i]]=i;
    	}
    	Pre(1);
    	for(int C=1,j=0;C<=k;C++,j^=1) {
    		for(int i=n;i>=1;i--)
    			dp[i][j]=max(dp[i+1][j],max(dp[i+sz[rk[i]]][j],dp[i+sz[rk[i]]][j^1]+w[rk[i]]));
    		ans=max(ans,dp[1][j]);
    	}
    	printf("%d",ans);
    }
    

    T4

    好家伙,看到子树修改,本蒟蒻直接一个树剖,好吧看错题+爆栈直接 WA 10

    正解:直接 BFS 即可,设 \(F_i\) 为从根到这个点有多少个打了标记,若 \(F_i+Color_i\) 是偶数就不用翻转

    #include<bits/stdc++.h>
    using namespace std;
    inline int Rd() {
    	register int x=0;
    	char C=getchar();
    	for(;C<'0'||C>'9';C=getchar()) ;
    	for(;C>'/'&&C<':';C=getchar()) x=(x<<1)+(x<<3)+(C^48);
    	return x;
    }
    const int N=500005;
    int n,x[N],vis[N],lst[N],nxt[N],to[N],f[N],q[N],head,tail=1;
    int main() {
    	freopen("tree.in","r",stdin);
    	freopen("tree.out","w",stdout);
    	n=Rd();
    	for(int i=1;i<=n;i++)x[i]=Rd();
    	for(int i=1,fr;i<n;i++) {
    		fr=Rd(),to[i]=Rd();
    		nxt[i]=lst[fr],lst[fr]=i;		
    	}
    	q[1]=1;
    	for(int u,flg;head<tail;) {
    		u=q[++head],flg=(f[u]+x[u])&1;
    		if(flg)vis[u]=1;
    		for(int i=lst[u],v;i;i=nxt[i])
    			f[v=to[i]]=f[u]+flg,q[++tail]=v;
    	}
    	for(int i=1;i<=n;i++)
    		if(vis[i])printf("%d ",i);
    }
    

    总结

    T1:学会打表

    T2:多去把式子转换一下

    T3:子树修改就想想 DFS 序,多考虑转换成 DP

    T4:不一味的想数据结构,考虑简化

  • 相关阅读:
    html5笔记
    CGI
    php和apache的关系和作用()
    sass最佳实践
    javascript笔记——jQuery插件开发的几种方式
    关于ajax解析
    http协议详解
    【python之路6】pycharm的使用
    【每日一linux命令7】用户及用户组
    【每日一linux命令6】命令中的命令
  • 原文地址:https://www.cnblogs.com/KonjakLAF/p/14501532.html
Copyright © 2020-2023  润新知