一个bool占1个字节,int 4字节 ,char 1字节 ,long long 8 字节 1KB = 1024B(字节) 1MB = 1024KB 1GB = 1024MB[NOI2014] 起床困难综合症
在这道题中,限制为256MB = 268,435,456个字节
long long tn[100005]; int on[100005]; int flag,flag1,flag2; bool vis[1000000000];
8 100005 +4 100005 + 1000000000 = 1,001,200,060
超限制了
而且有很多无用空间,导致本题直接从60 到 0。
sol:二进制数在三中操作中每一位都是独立的,不受其它位的影响。 我们希望原来的0都变1,如果原来的1不变 开两个量,一个所有位上都是0,另一个所有位上都是1,同时运算 运算结果逐个枚举,把两个量中的一都累加到ans中,并且注意那个全都是1的值不能大于M 时间复杂度位O(n)大中锋的游乐场
sol: 带环的最短路,套上一个k值属性,每一个点的距离权值可能对应多个k值,所以用二维数组捆绑 分层图也可以但是适用于终点固定在一层,这里没有限定最后的k,所以分层图没有必要
考試的時候沒有綁定
孙老板去旅馆区间修改,区间询问用线段树 本来用的一个sum表示这个区间的值,if(t[p].r - t[p].l + 1 == d && t[p].sum == d)来判断是否是这个区间 但二分该线段的时候,原来比如说 1 1 1 1 1 0我要4个 1 1 1 1 1 0 好了,本来成立的这样就不行了 所以一定要有三个量,pre,suf,maxn maxn 判断这个线段里是否存在这个长度 pre,suf是用来拼接成这个长度并确定x的位置jth的地獄門
const int N=4e2+5;
int t;
int g[N][N];//存图
int sum[N][N];//预处理每一列上的前缀和
int block[N];//上下边界全为1(不去顶角)+中间变成空白的次数
int bdr[N];//block[k-1]+右边界全为1(去掉顶角)的次数
int minn[N];//
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
scanf("%d",&g[i][j]);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
sum[i][j]=sum[i-1][j]+g[i][j];
int ans=n*m;
for(int i=1;i<=n-5+1;++i)
for(int j=i+5-1;j<=n;++j)
{
int len=j-i-1;
for(int k=1;k<=m;++k)
{
int tot=sum[j-1][k]-sum[i][k];//num(1)
block[k]=block[k-1]+tot;
bdr[k]=block[k]-tot+(len-tot);//模擬k -1
block[k]+=2-g[i][k]-g[j][k];
}
minn[m-3]=bdr[m];
for(int k=m-4;k;--k)
minn[k]=min(minn[k+1],bdr[k + 3]);//minn[k]=min(minn[k+1],ber[k + 3]);
for(int k=1;k<=m-3;++k)
{
int tot=sum[j-1][k]-sum[i][k];
tot=minn[k]-block[k]+(len-tot);
ans=min(ans,tot);
}
}
printf("%dn",ans);
return 0;
}
mmin[i]为i + 3 - 最后一列中bdr的最小值
bdr是1 - k列,k每次动,内部会变,对应的最小值也可能更小
mmin[i + 1]的范围比mmin[i]小,可以从mmin[i + 1]中找到最小值,但也会多出bdr[i + 3],(mmin[i]为i + 3 - 最后一列中bdr的最小值),所以要考虑bdr[i + 3]
预处理好后,tot为1列不包括顶角,tot重新赋值为••••此時的mmin[k]
这道题主要是想了解怎么用DP把枚举矩阵最大值的n4消为n3
7.14 sum用倍增求LCA,树上的前缀和,ans = sum[x] + sum[y] - 2 * sum[lca] + val[lca]反素数
结论题:1.任意一个合数都能看成若干个质数相乘,求因数个数套用排列组合结论公式。这样只要10个质数就能达到n的最大值,可以DFS
2.1 - n 中满足所有小于 n 且大于等于 1 的所有正整数的约数个数都小于 n 的约数个数的最大数,这其实就是求一个因数个数最大的最大数
3.如果两个数因数都是那个最大数,那么后面的那个数并不满足它前面的数都比它少,所以要取前面那个数
int num[1000]={0,2,3,5,7,11,13,17,19,23,29,31,33};
long long i,j,n,ans,best;
void dfs (long long x,long long y,int z){
if (x>best) {
best=x;
ans=y;
}
if(x==best&&ans>y)ans=y;//如果一个更小的数有相同的因数个数,那么他
if(z>11)return;
for(int i=1;i<=50;i++){
if(y*num[z]>n)break;
dfs(x*(i+1),y*num[z],z+1);
y*=num[z];
}
}
总结:一个长的判定条件可以分步去想,逐步缩小范围
[USACO07JAN] Balanced Lineup G
RMQ有线段树在线做法
ST离线做法
这道题用st就可以了
学长说要用树状数组拓展,似乎要加DFS递归,那这样还不如st呢,DFS容易写挂。总不会卡我这个吧。
注意ST查询是K不是K - 1
调了半天。
递增答案是单调的,想到二分,但是学长要求树状数组。
st : 1 3 5 2 4 3
ed : 1 3 5 7 8 9 3
数的值小于下标,那么一定要改。
离散化一下
显然读入可以离散化。离散化以后把DP的状态定义改变一下,设f_i
为当前算到的以i为结尾的LIS的ans。显然f[i] = max(f[i],f[j + 1])(j < i>)
用树状数组维护最大值O(nlogn)
求出LIS
用n - LIS即可
还要考虑去重问题,如果有多个数字相同v.erase(unique(v.begin(), v.end()), v.end());
那就全部清除掉,因为是实数,这里的数要不满足条件都可以卡在一个小数里,但多余的肯定都要改的,只保留第一个就可以了。
主要思路是将x提出来,得到x以此为树状数组的下标维护
细节很多
树的直径AB,设C点为k
ans = max(min(dis[a][k],dis[b][k]) + dis[a][b],ans);
难点就在每次只改一个而不是一个区间,但求得是区间最大值而且不是整个区间
所以每个大区间都要拆成小区间,那么类比之前做过的孙老板去旅馆,都要开一个前缀和后缀
首先,每条边会走两次,但是形成环后只用走一次,并加上1
可知,这个环越大越好
当在树的直径上加边时,环最大。
当k = 1时,找树的直径
当k = 2时,先找树的直径,在把第一个直径的边权全部改为-1,在找一个直径
相当可惜
这道题是典型的DAG,但我没看出来
我打的代码并没有处理好流污水的先后顺序,我用广搜会出现重复读边,或者先后顺序有问题
做的题实在太少了
这道题考场上20分
重写60分
那年省三六十分
注意edge数组开两倍因为可能是双向边
由题可得,受欢迎的奶牛只有可能是图中唯一的出度为零的强连通分量中的所有奶牛,所以若出现两个以上出度为0的强连通分量则不存在明星奶牛,因为那几个出度为零的分量的爱慕无法传递出去。那唯一的分量能受到其他分量的爱慕同时在分量内相互传递,所以该分量中的所有奶牛都是明星。
#include矩阵游戏using namespace std; const int N = 1e4 + 5,M = 5e4 + 5; int n,m,x,y,hd[N],idx,timestamp,top,cnt; int dfn[N],sum,res,siz[N],low[N],stk[N],out[N],id[N]; bool in_stk[N]; struct Edge{ int to,nxt; }edge[M]; void add(int x,int y) { edge[++ idx].to = y;edge[idx].nxt = hd[x];hd[x] = idx; } void tarjan(int u){ dfn[u] = low[u] = ++ timestamp; stk[ ++ top] = u, in_stk[u] = true; for (int i = hd[u];i; i = edge[i].nxt) { int j = edge[i].to; if (!dfn[j]) { tarjan(j); low[u] = min(low[u], low[j]); } else if (in_stk[j]) low[u] = min(low[u], low[j]); } if (dfn[u] == low[u]) { ++ cnt; int y; do { y = stk[top -- ]; in_stk[y] = false; id[y] = cnt; siz[cnt] ++ ; } while (y != u); } } int main() { scanf("%d%d",&n,&m); for(int i = 1;i <= m;++ i) { scanf("%d%d",&x,&y); add(x,y); } for(int i = 1;i <= n;++ i) if(!dfn[i])tarjan(i); for (int i = 1; i <= n; i ++ ) for (int j = hd[i]; j; j = edge[j].nxt) { int k = edge[j].to; int a = id[i], b = id[k]; if (a != b) out[a] ++ ; } for(int i = 1;i <= cnt;i ++) { if(!out[i]) { ++ sum; res += siz[i]; } if(sum >= 2){ res = 0; break; } } printf("%d",res); return 0; }
把他看成点与点的配对
用二分图的最大匹配去写
贪心如何去证?看性质,反证法,设变量,得到不等式
7.19 Hankson 的趣味题一道约数数论,gcd
上帝造题的七分钟 2 / 花神游历各国难点在于模拟开根号下取整的操作
因为1e12的数开方6次就变成了1,所以需要修改的次数实际上很少,用并查集可以跳过小于等于1的数,然后树状数组单点修改即可。
离谱···
核心代码
add(i,(t=(int)sqrt(a[i]))-a[i]); a[i]=t; fa[i]=(a[i]<=1)?i+1:i; i=(find(i)==i)?i+1:fa[i];国王
状压战略游戏
上司的舞会一样最大子树和
剪枝条就是不要 同上司的舞会中国象棋
dp[i][j][k]表示前i行,有j列只放了一个,有k列放了两个的方案数 第i行什么都不放:f[i][j][k]=f[i][j][k]+f[i−1][j][k] .放1个在空的列。根据状态很容易发现空的列是m-j-k。那么方案显然了:f[i][j][k]=f[i][j][k]+f[i−1][j−1][k]×(m−(j−1)−k) 放1个在有1个的列。f[i][j][k]=f[i][j][k]+f[i−1][j+1][k−1]×(j+1) 放2个都在空的列。这里就用到排列了 ······中国剩余定理(CRT)/ 曹冲养猪
裸体
#include[NOIP1999 提高组] 旅行家的预算#define ll long long ll n,a[16],m[16],Mi[16],mul=1,X; inline int read() { int f=1,x=0;char s=getchar(); while(!isdigit(s)){if(s=='-')f=-1;s=getchar();} while(isdigit(s)){x=x*10+s-'0';s=getchar();} return x*f; } void exgcd(ll a,ll b,ll &x,ll &y){ if(b==0){x=1;y=0;return ;} exgcd(b,a%b,x,y); int z=x;x=y,y=z-y*(a/b); } int main(){ n=read(); for(int t=1;t<=n;++t){ int M=read();m[t]=M; mul*=M; a[t]=read(); } for(int t=1;t<=n;++t){ Mi[t]=mul/m[t]; ll x=0,y=0; exgcd(Mi[t],m[t],x,y); X+=a[t]*Mi[t]*(x<0?x+m[t]:x); } printf("%lld",X%mul); return 0; }
每次先用最便宜的油装满,有更便宜的就换,最后退油钱
用优先队列维护即可,细节题。
方程错了还有16p,逆天
树形背包,把能留几条边看成空间即可。
第一次看到换根
新根的子树(包括自己)深度都减少了1,原根的子树的深度都增加了1。
也就是说:对于x,它的父亲是y。
f[x]=f[y]-size[x]+n-size[x].
逆推区间DP
[HNOI2010]合唱队逆推分类讨论区间DP
if(a[i]a[i])f[i][j][1]+=f[i][j-1][0]; if(a[j]>a[j-1])f[i][j][1]+=f[i][j-1][1];[CQOI2007]涂色
逆推 + 小贪心
7.21 货车运输先根据题目判断有走最大路,小路被视为多余,所以要建最大生成树。
然后套倍增或树剖来维护
#include三色二叉树using namespace std; #define maxn 100005 #define inf 999999999 struct Edge1{ int x,y,dis; }edge1[maxn]; struct Edge2{ int to,nxt,w; }edge2[maxn]; int cnt,n,m,hd[maxn],dep[maxn],f[maxn],fa[maxn][21],w[maxn][21],deep[maxn]; bool vis[maxn]; void add(int x,int y,int z) { edge2[++ cnt].nxt = hd[x];edge2[cnt].to = y;edge2[cnt].w = z;hd[x] = cnt; } bool cmp(Edge1 x,Edge1 y){ return x.dis > y.dis; } int find(int x) { if(f[x] == x) return f[x]; return f[x] = find(f[x]); } void kruskal() { sort(edge1 + 1,edge1 + m + 1,cmp); for(int i= 1;i <= n;++ i) f[i] = i; for(int i = 1;i <= m;++ i) { if(find(edge1[i].x) != find(edge1[i].y)) { f[find(edge1[i].x)] = find(edge1[i].y); add(edge1[i].x,edge1[i].y,edge1[i].dis); add(edge1[i].y,edge1[i].x,edge1[i].dis); } } return; } void dfs(int node){ vis[node] = true; for(int i = hd[node];i;i = edge2[i].nxt) { int to = edge2[i].to; if(vis[to])continue; deep[to] = deep[node] + 1; fa[to][0] = node; w[to][0] = edge2[i].w; dfs(to); } return; } int lca(int x,int y) { if(find(x) != find(y))return -1; int ans = inf; if(deep[x] > deep[y]) swap(x,y); for(int i=20; i>=0; i--) if(deep[fa[y][i]]>=deep[x]){ ans=min(ans, w[y][i]); y=fa[y][i]; } if(x == y) return ans; for(int i = 20;i >= 0;i --) { if(fa[x][i] != fa[y][i]) { ans = min(ans,min(w[x][i],w[y][i])); x = fa[x][i]; y = fa[y][i]; } } ans=min(ans, min(w[x][0], w[y][0])); return ans; } int main() { int x,y,z,q; scanf("%d%d",&n,&m); for(int i=1; i<=m; i++){ scanf("%d%d%d",&x,&y,&z); edge1[i].x=x; edge1[i].y=y; edge1[i].dis=z; } kruskal(); for(int i=1; i<=n; i++) if(!vis[i]){ deep[i]=1; dfs(i); fa[i][0]=i; w[i][0]=inf; } for(int i=1; i<=20; i++) for(int j=1; j<=n; j++){ fa[j][i]=fa[fa[j][i-1]][i-1]; w[j][i]=min(w[j][i-1], w[fa[j][i-1]][i-1]); } scanf("%d",&q); for(int i=1; i<=q; i++){ scanf("%d%d",&x,&y); printf("%dn",lca(x,y)); } return 0; }
设 f[i][0/1/2]表示第i个节点染成 (绿 / 红 / 蓝) 时,它为根的子树中最多有多少绿色节点;g[i][0/1/2] 表示最少
f[x][0] = max(f[son1][1] + f[son2][2], f[son1][2] + f[son2][1]) + 1; //绿色+1 f[x][1] = max(f[son1][0] + f[son2][2], f[son1][2] + f[son2][0]); f[x][2] = max(f[son1][0] + f[son2][1], f[son1][1] + f[son2][0]); g[x][0] = min(g[son1][1] + g[son2][2], g[son1][2] + g[son2][1]) + 1; g[x][1] = min(g[son1][0] + g[son2][2], g[son1][2] + g[son2][0]); g[x][2] = min(g[son1][0] + g[son2][1], g[son1][1] + g[son2][0]);



