([NOIp2014]) 螺旋矩阵
(Sol)
直接模拟,一次走一整行或者一整列.复杂度(O(n)).
(Code)
#include<bits/stdc++.h>
#define il inline
#define Ri register int
#define go(i,a,b) for(Ri i=a;i<=b;++i)
#define yes(i,a,b) for(Ri i=a;i>=b;--i)
#define e(i,u) for(Ri i=b[u];i;i=a[i].nt)
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define db double
#define inf 2147483647
using namespace std;
il int read()
{
Ri x=0,y=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*y;
}
int n,s,x,z,y,nx,ny,tx,ty,nd,ex,ey,nw;
il bool ck()
{
Ri x1=nx,x2=tx,y1=ny,y2=ty;
if(x1>x2)swap(x1,x2);if(y1>y2)swap(y1,y2);
return (ex>=x1 && ex<=x2 && ey>=y1 && ey<=y2);
}
il int calc(){return abs(nx-ex)+abs(ny-ey);}
int main()
{
n=read(),ex=read(),ey=read();
if(ex==1 && ey==1){printf("1
");return 0;}
nx=ny=nd=1;nw=1;
while(nd<=n*n)
{
if(nw==1){tx=nx,ty=n-y;++s;}
if(nw==2){tx=n-x,ty=ny;++y;}
if(nw==3){tx=nx,ty=z+1;++x;}
if(nw==4){tx=s+1,ty=ny;++z;}
if(ck()){printf("%d
",nd+calc());break;}
nd+=abs(nx-tx)+abs(ny-ty);nx=tx,ny=ty;++nw;if(nw>4)nw=1;
}
return 0;
}
([NOIp2014])子矩阵
(Sol)
朴素地搜索可以获得(80pts).想想搜索的优化其实并不多(其实是我会的很少).所以这题可以直接记忆化搜索.具体来说,先把要选的行搜出来,然后搜要选的列的时候记忆化就行.
(upd:)似乎搜行的时候也可以记忆化.不过搜列记忆化足以通过此题.
(Code)
#include<bits/stdc++.h>
#define il inline
#define Ri register int
#define go(i,a,b) for(Ri i=a;i<=b;++i)
#define yes(i,a,b) for(Ri i=a;i>=b;--i)
#define e(i,u) for(Ri i=b[u];i;i=a[i].nt)
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define db double
#define inf 2100000000
using namespace std;
il int read()
{
Ri x=0,y=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*y;
}
const int N=20;
int n,m,r,c,a[N][N],b[N],d[N],as=inf,ct,f[20][20];
il void init(){mem(f,-1);}
il int dfs2(Ri nw,Ri lst,Ri sum)
{
if(nw>r)return 0;
Ri ret=inf;
if(f[nw][lst]!=-1)return f[nw][lst];
go(i,lst+1,n-r+nw)
{
d[nw]=i;Ri tmp=0;bool fl=1;
go(j,2,c){tmp+=abs(a[i][b[j]]-a[i][b[j-1]]);if(tmp>=as){fl=0;break;}}
if(!fl)continue;
if(nw!=1)go(j,1,c){tmp+=abs(a[i][b[j]]-a[d[nw-1]][b[j]]);if(tmp>=as){fl=0;break;}}
if(!fl)continue;
ret=min(ret,dfs2(nw+1,i,tmp)+tmp);
}
return f[nw][lst]=ret;
}
il void dfs1(Ri nw,Ri lst)
{
if(nw>c){init();as=min(dfs2(1,0,0),as);return;}
go(i,lst+1,m-c+nw)
b[nw]=i;dfs1(nw+1,i);
}
int main()
{
n=read(),m=read(),r=read(),c=read();
go(i,1,n)go(j,1,m)a[i][j]=read();
dfs1(1,0);
printf("%d
",as);
return 0;
}
([NOIp2015]) 推销员
(Sol)
一个比较直接的想法是对于每个询问,枚举走得最远的位置,然后其他的直接选(这里可以用线段树维护一下)....这样做的复杂度大约是(O(n^2logn)),获得(60pts)问题不大.
这样考虑:先直接选择(A_i)前(X)大的.然后尝试舍去第(X)大的(A_i)来选择一个更远的.设选(A_i)前(X)大的贡献是(sum A+D*2),现在要舍弃(x)来换取(y),那么这样做可以使得贡献增加((D_y-D)*2-A_x+A_y).选取一个贡献最大的即可.实际上只要选取(D_y*2+A_y)最大的即可,这样可以直接用一个数组维护.
(Code)
#include<bits/stdc++.h>
#define il inline
#define Ri register int
#define go(i,a,b) for(Ri i=a;i<=b;++i)
#define yes(i,a,b) for(Ri i=a;i>=b;--i)
#define e(i,u) for(Ri i=b[u];i;i=a[i].nt)
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define db double
#define inf 2147483647
using namespace std;
il int read()
{
Ri x=0,y=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*y;
}
const int N=100010;
int n,ct,sa[N],maxd[N],maxf[N];
struct nd{int a,d;}t[N];
il bool cmp1(nd x,nd y){return x.d<y.d;}
il bool cmp(nd x,nd y){return x.a>y.a;}
int main()
{
n=read();
go(i,1,n)t[i].d=read();
go(i,1,n)t[i].a=read();
sort(t+1,t+n+1,cmp);
go(i,1,n)if(t[i].a==0){n=i-1;break;}
go(i,1,n)sa[i]=sa[i-1]+t[i].a,maxd[i]=max(maxd[i-1],t[i].d);
yes(i,n,1)maxf[i]=max(maxf[i+1],t[i].d*2+t[i].a);
go(i,1,n)printf("%lld
",max(sa[i]+maxd[i]*2,sa[i-1]+maxf[i+1]));
return 0;
}
([NOIp2015])求和
(Sol)
简化下题意,纸带的分数是:(sum_{i<j,(j-i)&1=0}(i+j)(num_i+num_j)).
直接枚举相同颜色的(i,j),(check (j-i))是否为偶数累加答案,可以获得(80pts).如果可以省去(check())这一步就好了.其实可以按照奇偶分组,同一组内,只要颜色相同就可以对答案产生贡献.
假设([l,r])区间都是一种颜色,答案累加:
(sum_{i=l}^{r}sum _{j=i+1}^r(i+j)(num_i+num_j))
(=sum_{i=l}^r i*num_i*(r-l-1)+sum_{i=l}^{r}sum _{j=i}^r i*num_j)
(=sum_{i=l}^r i*num_i*(r-l-1)+sum_{i=l}^{r}i *sum_{i=l}^r num_i)
注意特判(l=r)的情况.
(Code)
#include<bits/stdc++.h>
#define il inline
#define Ri register int
#define go(i,a,b) for(Ri i=a;i<=b;++i)
#define ll long long if(ct>1)as+=1LL*s3*(ct-2)%mod+1LL*s1*s2%mod;as%=mod;s1=s2=s3=ct=0;
using namespace std;
il int read()
{
Ri x=0,y=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*y;
}
const int N=100010,mod=10007;
int n,m,as,n1,n2;
struct nd{int num,col,pos;}a[N],b[N];
il bool cmp(nd x,nd y){return x.col<y.col;}
int main()
{
freopen("1.in","r",stdin);
n=read(),m=read();
go(i,1,n)if(i&1)a[++n1].num=read(),a[n1].pos=i;else b[++n2].num=read(),b[n2].pos=i;
n1=n2=0;
go(i,1,n)if(i&1)a[++n1].col=read();else b[++n2].col=read();
sort(a+1,a+n1+1,cmp);sort(b+1,b+n2+1,cmp);
Ri s1=0,s2=0,s3=0,ct=0;
go(i,1,n1)
{
s1+=a[i].num,s2+=a[i].pos,s3+=1LL*a[i].num*a[i].pos%mod,++ct;
s1%=mod,s2%=mod,s3%=mod;
if(a[i].col==a[i+1].col)continue;
if(ct>1)as+=1LL*s3*(ct-2)%mod+1LL*s1*s2%mod;as%=mod;s1=s2=s3=ct=0;
}
s1=s2=s3=ct=0;
go(i,1,n2)
{
s1+=b[i].num,s2+=b[i].pos,s3+=1LL*b[i].num*b[i].pos%mod,++ct;
s1%=mod,s2%=mod,s3%=mod;
if(b[i].col==b[i+1].col)continue;
if(ct>1)as+=1LL*s3*(ct-2)%mod+1LL*s1*s2%mod;as%=mod;s1=s2=s3=ct=0;
}
printf("%d
",as);
return 0;
}
([NOIp2016])海港
(Sol)
开一个队列存以当前时间为结束的(24)小时内到达的游客(包括时间和国际).一个数组(s[i])表示当前队列中国籍为(i)的游客有多少.一个变量(nw)记录当前队列中有多少不同国籍的游客.因为保证输入时间是递增的可以在线做,没必要离线做.每次来了一条新船,就把队首时间不合法的游客移除,顺便维护下(s[i]),如果有(s[i])因此变为(0)了,那么就(--nw).然后在队尾加入这条船的游客,维护$s[i] (,若有)s[i]$因此变为(1),那么(++nw).最后直接输出(nw),进入下一次循环即可.
(Code)
#include<bits/stdc++.h>
#define il inline
#define Ri register int
#define go(i,a,b) for(Ri i=a;i<=b;++i)
using namespace std;
il int read()
{
Ri x=0,y=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*y;
}
const int N=100010;
int n,ct,q[N*3],l=1,r,s[N*3],nw;
struct nd{int t,k;}a[N*3];
int main()
{
n=read();
go(i,1,n)
{
Ri t=read(),k=read();
go(i,1,k)
{
Ri kk=read();
a[++ct]=(nd){t,kk};q[++r]=ct;
++s[kk];if(s[kk]==1)++nw;
}
while(a[q[l]].t<=t-86400){if(!(--s[a[q[l]].k]))--nw;++l;}
printf("%d
",nw);
}
return 0;
}
([NOIp2017]) 棋盘
$Sol $
看起来十分像一道搜索题.需要记录地状态:当前在哪一个格子,已经花费多少金币,当前可不可以使用魔法.还要标记走过的格子.这样没有任何剪枝地搜就可以获得(55pts)的好成绩.
加一个最优性剪枝就可以多获得(10pts).
记一个(rem[i][j])表示从((1,1))到((i,j))这个点花的最小代价,如果再搜到这里的时候代价大于它,那么就(return).这样做不仅可以剪枝,而且还可以少记一个(vis),于是少维护(vis)又可以加快速度.
(upd):这样做当且仅当再走到这里的时候代价一定大于原来的情况,可是这题不一定叭?假如有一个同颜色的环,这样还是会死循环下去.
(再upd),其实这里是可以按上面的做法的,只是一定是代价大于等于的时候(return),而且这样的话一定不可以记(vis).
另外一个要注意的点(特别是如果要维护$vis (的话):不优的情况与其)dfs(下去再判掉不如**在)dfs$之前就判掉.**
(Code)
#define il inline
#define Ri register int
#define go(i,a,b) for(Ri i=a;i<=b;++i)
#define yes(i,a,b) for(Ri i=a;i>=b;--i)
#define e(i,u) for(Ri i=b[u];i;i=a[i].nt)
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define db double
#define inf 2147483647
using namespace std;
il int read()
{
Ri x=0,y=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*y;
}
const int N=110;
int m,n,col[N][N],dx[4]={1,0,-1,0},dy[4]={0,1,0,-1},as=inf,rem[N][N];
il void dfs(Ri nx,Ri ny,Ri color,Ri sum,bool mag)
{
if(sum>=as)return;
if(nx==ny && nx==m){as=min(as,sum);return;}
rem[nx][ny]=sum;
go(i,0,3)
{
Ri tx=nx+dx[i],ty=ny+dy[i];
if(tx<1 || tx>m || ty<1 || ty>m)continue;
if(col[tx][ty])
{
if(col[tx][ty]==color && (rem[tx][ty]==-1 || sum<rem[tx][ty]))
{dfs(tx,ty,col[tx][ty],sum,1);}
else if(rem[tx][ty]==-1 || sum+1<rem[tx][ty])
{dfs(tx,ty,col[tx][ty],sum+1,1);}
}
else if(mag && (rem[tx][ty]==-1 || sum<rem[tx][ty]))
{dfs(tx,ty,color,sum+2,0);}
}
}
int main()
{
m=read(),n=read();mem(rem,-1);
go(i,1,n){Ri x=read(),y=read(),c=read();col[x][y]=c+1;}
dfs(1,1,col[1][1],0,1);
if(as==inf)as=-1;
printf("%d
",as);
return 0;
}
([NOIp2017]) 跳房子
(Sol)
首先判断有无解就是把所有的分数加起来与(k)比较.
部分分是可以搜索的,就搜索出依次跳到哪些格子可以至少获得(k)分,然后统计一下答案就可以了.这样就获得了(20pts).
觉得可以(dp.jpg).
(f[i][j])表示前(i)个格子,且选了第(i)个格子,花费(j)改进,可以获得的最大价值.
(f[i][j]=max (f[k][g])+a[i],k<i 且 abs(x[i]-x[k]-d)<=j且g<=j).
然后(max(f[k][g]))这里可以用单调队列优化一下?
然而这样并不足以(AC),时间空间上都有问题.如果能在(f)上省去一维就好了.真的不会,于是去看了题解.
理解题解之后的:其实上述(dp)的第二维相当于在枚举最小花费,又应为最小花费显然可以二分.所以简单来说:首先二分最小花费,然后对这个最小花费(dp),最后统计答案.(over).
(QwQ)
写代码自闭了,真的要仔仔细细认认真真地思考一遍再写代码吖.几乎每次对代码掉以轻心,觉得直接写就好了的时候就会卡很久,到处出错,实际花的时间多得多.别忘了你是个菜鸡啊!写代码要沉着,冷静,放慢速度.
(Code)
#include<bits/stdc++.h>
#define il inline
#define Ri register int
#define go(i,a,b) for(Ri i=a;i<=b;++i)
#define yes(i,a,b) for(Ri i=a;i>=b;--i)
#define e(i,u) for(Ri i=b[u];i;i=a[i].nt)
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define inf 2147483647
using namespace std;
il int read()
{
Ri x=0,y=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*y;
}
const int N=500010;
int n,d,k,x[N],s[N],f[N],as=inf,L,R,mid,q[N];
il bool Ck()
{
ll sum=0;Ri las=0;
yes(i,n,1)if(s[i]>0){sum+=s[i];R=max(R,abs(x[i]-x[las]-d));las=i;};
return (sum>=k);
}
il bool ck(Ri g)
{
go(i,1,n)f[i]=-inf;f[0]=0;
Ri l=1,r=0,las=0;
go(i,1,n)
{
while(l<=r && x[q[l]]<x[i]-d-g)l++;
while(las<=n && x[las]<x[i]-d-g)las++;
while(las<i && x[las]<=x[i]-d+g )
{
if(f[las]==-inf){++las;continue;}
while(l<=r && f[q[r]]<=f[las])r--;
q[++r]=las;++las;
}
if(l<=r)f[i]=f[q[l]]+s[i];
if(f[i]>=k)return 1;
}
return 0;
}
int main()
{
n=read(),d=read(),k=read();
go(i,1,n)x[i]=read(),s[i]=read();
if(!Ck()){printf("-1
");return 0;}
while(L<=R)
{
mid=(L+R)>>1;
if(ck(mid))as=mid,R=mid-1;
}
printf("%d
",as);
return 0;
}
([NOIp2018])对称二叉树
(Sol)
最初以为只要中序遍历是回文串就是对称二叉树,于是写了下只有(64pts).其实上面那个结论错得挺显然的.应该是这样的:首先是根的左孩子和右孩子相等,然后左孩子的左孩子和右孩子的右孩子相等,左孩子的右孩子和右孩子的左孩子相等.....这个可以以每个结点作为根(check)一次.本来打算先写个暴力,结果就(AC)辣.感觉复杂度似乎是(O(nlogn)).
还有个(O(n))的(hash)做法,其实我感觉就是我最开始那个回文串做法的改进.可以直接(hash)树的形态,举个栗子:一棵三个结点的二叉树(root,lson,rson),那么其(hash)值为(lson*bas1+rson*bas2+root*bas3).要计算两个(hash)值,一个是先走左儿子的中序遍历值(hash1),一个是先走右儿子的(hash2),对于(root),若(hash1[lson]=hash2[rson])那么就是对称二叉树.另外,记一下(TJ)中用的(bas),(b1=999999751,b2=299999827,b3=100000007,m1=89999794200117649,m2=999999786000011449).
(Code)
#include<bits/stdc++.h>
#define il inline
#define Ri register int
#define go(i,a,b) for(Ri i=a;i<=b;++i)
#define yes(i,a,b) for(Ri i=a;i>=b;--i)
#define e(i,u) for(Ri i=b[u];i;i=a[i].nt)
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define db double
#define inf 2147483647
using namespace std;
il int read()
{
Ri x=0,y=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*y;
}
const int N=1000010;
int n,l[N],r[N],w[N],ct,sz[N],as;
il void dfs(Ri nw)
{
sz[nw]=1;
if(l[nw]>-1)dfs(l[nw]),sz[nw]+=sz[l[nw]];
if(r[nw]>-1)dfs(r[nw]),sz[nw]+=sz[r[nw]];
}
il int ck(Ri x,Ri y)
{
if(x==-1 && y==-1)return 1;
if(x==-1 || y==-1)return 0;
if(w[x]!=w[y])return 0;
if(ck(l[x],r[y]) && ck(r[x],l[y]))return 1;
return 0;
}
int main()
{
n=read();
go(i,1,n)w[i]=read();
go(i,1,n)l[i]=read(),r[i]=read();
dfs(1);
go(i,1,n)if(ck(l[i],r[i]))as=max(as,sz[i]);
printf("%d
",as);
return 0;
}