欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

3.18-3.24周总结

程序员文章站 2022-03-10 07:50:51
...
	为迎接JSCPC选拔(反正没多大机会),而进行的周训练.

1.静下心来,能学会的还是能学会的学不会就拉倒.
2.捞钱杯杀我.明年再来
3.最菜实锤


本周学习了三部分内容,学的都很肤浅.

数位DP(只会简单题)

预处理+dp.
根据,题目要求,确定dp数组的维数,简单推到得出状态转移方程.

例题:不要62,4
题意:给定[n,m] 之前的不包含 "62"(连续),且不包含"4"的数的个数

预处理内容
首先需要一维来记录数的位数,或者说从前一位推下一位的状态.
因为与位上的数字有关,所以还需要一维来记录位上的数字.
现在我们要推出,dp [i][j],即求i位数,最高为j的合法的个数.首先,显然
dp[i][4]=0;最高位为4,合法数都为0;
这个最高位的限制条件取决于前一位是否是2,若前一位 ( i - 1 ) 最高位2,即限制前一位不能选6.否则,无论前一位取何值,都合法.

得出
j ! = 6 ,dp[ i ][ j ]=Sigma( dp[ i - 1 ][ k ]) ****;
j=6 ,dp[ i ][ j ]=Sigma( dp[ i - 1 ][ k ]) - dp[ i - 1 ][ 2 ];
j=4,dp[i][j]=0;

#include<bits/stdc++.h>
#define ll long long

using namespace std;
int T,n,m,tot,cnt=1;	char c;bool flag=0;
void rd(){
	 #ifdef Dmymax_ly
	freopen("test.in","r",stdin);
	#endif
}
ll dp[25][12];

void init(){//dp[i][j] 表示i位数字,最高位为 j 的合法数.
	memset(dp,0,sizeof(dp));
	dp[0][0]=1;
	for(int i=1;i<=10;i++){
		for(int j=0;j<=9;j++){
			if(j==4) dp[i][j]=0;
			else if(j==6){
                for(int k=0;k<=9;k++)       dp[i][j]+=dp[i-1][k];
                dp[i][j]-=dp[i-1][2];
			}
			else {
				for(int k=0;k<=9;k++){
    	           	dp[i][j]+=dp[i-1][k];
				}
			}
		}
	}
}
int dig[11];
ll solve(int x){
	cnt=0;
	memset(dig,0,sizeof(dig));
	while(x){
		dig[++cnt]=x%10;
		x/=10;
	}
	dig[cnt+1]=0;
	ll sum=0;
	for(int i=cnt;i>=1;i--){
		for(int j=0;j<dig[i];j++){
		    if (j!=4 && !(dig[i+1]==6 && j==2)){
				sum+=dp[i][j];
			}
		}
        if (dig[i]==4) break;
       if (dig[i+1]==6 && dig[i]==2) break;
	}
	return sum;
}

int main(){
	init();
	while(scanf("%d%d",&n,&m)&&(n||m)){
		printf("%lld\n",solve(m+1)-solve(n));
	}
	return 0;
}


线段树(只会模板题)

1.处理建树,与维护区间的问题 手撸不会弃了
线段树递归处理问题,根据mid与待求区间的左右关系的问题.

递归到底层,再传导到上层的方法.
2.几个基本函数

建树build函数


    void build(int l,int r,int rt){
	if(l==r){
		scanf("%d",&Tree[rt].sum);
		return;
	}
	int m=(l+r)/2;
	build(l,m,rt<<1);
	build(m+1,r,rt<<1|1);
	pushup(rt);
}

更新函数(向上)

void pushup(int rt){
	Tree[rt].sum=Tree[rt<<1].sum+Tree[rt<<1|1].sum;
	Tree[rt].maxn=max(Tree[tr>>1].maxn+Tree[tr>>1|1].maxn);
}

更新函数(向下)

void pushdown(int rt,int m) {
       if (Tree[rt].lazy) {
              Tree[rt<<1].lazy += Tree[rt].lazy;
              Tree[rt<<1|1].lazy += Tree[rt].lazy;
              Tree[rt<<1].sum += Tree[rt].lazy * (m - (m >> 1));
              Tree[rt<<1|1].sum += Tree[rt].lazy * (m >> 1);
              Tree[rt].lazy = 0;
       }
}

单点修改

void change(int l,int r,int k,int x,int rt){
	if(l==r){
		Tree[rt].sum+=x;
		return;
	}
	int m=(l+r)/2;
	if(k<=m) change(l,m,k,x,rt<<1);
	else change(m+1,r,k,x,rt<<1|1);
	pushup(rt);
}

区间查询

int query(int L,int R,int l,int r,int rt){
	if(L<=l&&R>=r){
		return Tree[rt].sum;
	}
	int res=0;
	int m=(l+r)/2;
	if(L<=m) res+=query(L,R,l,m,rt<<1);
	if(R>=m+1) res+=query(L,R,m+1,r,rt<<1|1);
	return res;
}

区间修改

void update(int L,int R,int c,int l,int r,int rt) {
       if (L <= l && r <= R) {
              Tree[rt].lazy += c;
              Tree[rt].sum += (ll)c * (r - l + 1);
              return ;
       }
       pushdown(rt , r - l + 1);
       int m = (l + r) >> 1;
       if (L <= m) update(L , R , c , l , m , rt << 1);
       if (m < R) update(L , R , c , m + 1 , r , rt << 1 | 1);
       pushup(rt);
}
	敌兵布阵
	题意:简单的单点修改,区间查询.
#include<bits/stdc++.h>
#define ll long long
#define scafn scanf
#define prinft printf
const int maxx=55500;
using namespace std;
struct p{
	int sum;
}Tree[maxx<<2];
int T,n,m,tot,cnt,x,y;	char s[5];
void pushup(int rt){
	Tree[rt].sum=Tree[rt<<1].sum+Tree[rt<<1|1].sum;
	//Tree[rt].maxn=max(Tree[tr>>1].maxn+Tree[tr>>1|1].maxn);
}
void build(int l,int r,int rt){
	if(l==r){
		scanf("%d",&Tree[rt].sum);
		return;
	}
	int m=(l+r)/2;
	build(l,m,rt<<1);
	build(m+1,r,rt<<1|1);
	pushup(rt);
}
void change(int l,int r,int k,int x,int rt){
	if(l==r){
		Tree[rt].sum+=x;
		return;
	}
	int m=(l+r)/2;
	if(k<=m) change(l,m,k,x,rt<<1);
	else change(m+1,r,k,x,rt<<1|1);
	pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
	if(L<=l&&R>=r){
		return Tree[rt].sum;
	}
	int res=0;
	int m=(l+r)/2;
	if(L<=m) res+=query(L,R,l,m,rt<<1);
	if(R>=m+1) res+=query(L,R,m+1,r,rt<<1|1);
	return res;
}
void rd(){
	 #ifdef Dmymax_ly
	freopen("test.in","r",stdin);
	#endif
}
int main(){
	rd();
	scanf("%d",&T);
	int num=T;
	while(T--){
		printf("Case %d:\n",num-T);
		scanf("%d",&n);
		build(1,n,1);
		while(scanf("%s",s)){
			if(s[0]=='E') break;
			if(s[0]=='A'){
				scanf("%d %d",&x,&y);
				change(1,n,x,y,1);
			}
			if(s[0]=='S'){
				scanf("%d %d",&x,&y);
				change(1,n,x,-y,1);
			}
			if(s[0]=='Q'){
				scanf("%d %d",&x,&y);
				printf("%d\n",query(x,y,1,n,1));
			}
		}
	}
	return 0;
}

	A simple problem 
	题意:区间修改+区间查询	
	注意点: [**cin读入会超时,应该用scanf**]
	(贴假代码怕是要被打死)
#include<stdio.h>
#include<iostream>
#define ll long long
using namespace std;
const int maxx = 211111;
int T,n,m,tot,cnt,x,y,v; char s[5];
struct p{
	ll maxn,sum;
	ll lazy;
}Tree[maxx<<2];
void pushup(int rt) {
       Tree[rt].sum = Tree[rt<<1].sum + Tree[rt<<1|1].sum;
}
void pushdown(int rt,int m) {
       if (Tree[rt].lazy) {
              Tree[rt<<1].lazy += Tree[rt].lazy;
              Tree[rt<<1|1].lazy += Tree[rt].lazy;
              Tree[rt<<1].sum += Tree[rt].lazy * (m - (m >> 1));
              Tree[rt<<1|1].sum += Tree[rt].lazy * (m >> 1);
              Tree[rt].lazy = 0;
       }
}
void build(int l,int r,int rt) {
       Tree[rt].lazy = 0;
       if (l == r) {
              scanf("%lld",&Tree[rt].sum);
              return ;
       }
       int m = (l + r) >> 1;
       build(l , m , rt << 1);
       build(m + 1 , r , rt << 1 | 1);
       pushup(rt);
}
void update(int L,int R,int c,int l,int r,int rt) {
       if (L <= l && r <= R) {
              Tree[rt].lazy += c;
              Tree[rt].sum += (ll)c * (r - l + 1);
              return ;
       }
       pushdown(rt , r - l + 1);
       int m = (l + r) >> 1;
       if (L <= m) update(L , R , c , l , m , rt << 1);
       if (m < R) update(L , R , c , m + 1 , r , rt << 1 | 1);
       pushup(rt);
}
ll query(int L,int R,int l,int r,int rt) {
       if (L <= l && r <= R) {
              return Tree[rt].sum;
       }
       pushdown(rt , r - l + 1);
       int m = (l + r) >> 1;
       ll res = 0;
       if (L <= m) res += query(L , R , l , m , rt << 1);
       if (m < R) res += query(L , R , m + 1 , r , rt << 1 | 1);
       return res;
}
void rd(){
	 #ifdef Dmymax_ly
	freopen("test.in","r",stdin);
	#endif
}
int main(){
    cin>>n>>m;
    build(1 , n , 1);
    while (m--) {
		cin>>s;
		if (s[0] == 'Q') {
			cin>>x>>y;
			cout<<query(x , y , 1 , n , 1)<<endl;
		}else{
			cin>>x>>y>>v;
			update(x , y , v , 1 , n , 1);
		}
	}return 0;
}

博弈

NP 状态的确定+大力猜结论+玄学SG

	NP状态的转换
	若该状态的所有后继状态皆为必败态,则该点必败.
	若该状态的所有后继状态存在一必胜态,则该点必胜.
	
	例题 II with GG
	题意:轮流移动棋子,往下,左,左下.最先将棋子移动到原点处胜.

给定的 n 和 m 不大,所以直接由起点推出棋盘上的每一个点的状态(先手胜为1)
对于第一行 ( 坐标系 ) 由于只能左移,简单易的列数为奇数先手败,偶数则胜;第一列,亦然.
若 vis[ i - 1 ] [ j - 1 ]== 1 || vis[ i ] [ j - 1]==1||vis[ i - 1 ][ j - 1 ] == 1,则 v i s [ i ][ j ]=1
若 vis[ i - 1 ] [ j - 1 ]== 0 || vis[ i ] [ j - 1]==0 || vis[ i - 1 ][ j - 1 ] == 0,则 v i s [ i ][ j ]=0

	大力猜结论,暴力出奇迹

写完蓝桥杯,发现自己暴力算法既然学的都不行

	玄学SG
	SG 函数是,即mex函数,是定义在集合上的一个函数.不属于集合内的最小自然数.如,mex{ 0 ,2 , 3 , 5 }=1; mex{ 0 , 1  , 2 }=3;(0是自然数) 
	规定SG(0)=0;

这里就直接贴一下网址

[ 博弈论入口 ]https://www.cnblogs.com/Wolfycz/p/8430991.html


很显然,这周很水.

	                                                                                 2019.3.24
																			         		Dmymax_ly
相关标签: 总结