3.18-3.24周总结
为迎接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