A. Two Rabbits
题意:两只兔子分别在xy,相向而跳,一个一次跳a,另一个一次跳b,问能不能相遇,多久相遇。
思路:直接看(y-x)能否被(a+b)整除即可。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 using namespace std; 17 int T; 18 int x,y,a,b; 19 int main(){ 20 // freopen("in.txt","r",stdin); 21 rd(T); 22 while(T--){ 23 rd(x);rd(y);rd(a);rd(b); 24 if((y-x) % (a+b))printf("-1 "); 25 else printf("%d ",(y-x)/(a+b)); 26 } 27 return 0; 28 } 29 /**/
B. Longest Palindrome
题意:给n(100)个长度为m(50)的字符串,选择一些组合成一个回文串,输出最长的那个。
思路:因为长度都一样,尽可能多选即可,每次从中选出两个互为回文的串加到最终串的开始和最后,直到选不出来为止,然后再看看能不能选出一个自身为回文串的放到最终串的中间即可。在寻找互为回文串的过程中可以暴力也可以用set优化。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=105; 17 const int M=55; 18 using namespace std; 19 int n,m; 20 char s[N][M]; 21 bool tag[N]; 22 int sta[N],top; 23 int main(){ 24 // freopen("in.txt","r",stdin); 25 rd(n);rd(m); 26 for(int i=1;i<=n;i++)scanf("%s",s[i]); 27 for(int i=1;i<n;i++){ 28 for(int j=i+1;j<=n;j++){ 29 bool flg=0; 30 for(int k=0;k<m;k++)flg =flg | (s[i][k] != s[j][m-k-1]); 31 if(!tag[i] && !tag[j] && !flg)tag[i]=tag[j]=1,sta[++top]=i; 32 } 33 } 34 bool ghb=0; 35 for(int i=1;i<=n;i++) 36 if(!tag[i]){ 37 bool flg=0; 38 for(int j=0;j<m;j++)flg=flg | (s[i][j] != s[i][m-j-1]); 39 if(!tag[i] && !flg){sta[++top]=i;ghb=1;break;} 40 } 41 printf("%d ",m*(2*top-ghb)); 42 for(int i=1;i<=top;i++)printf("%s",s[sta[i]]); 43 for(int i=top-ghb;i>=1;i--) 44 for(int j=m-1;j>=0;j--) 45 printf("%c",s[sta[i]][j]); 46 puts(""); 47 return 0; 48 } 49 /**/
C. Air Conditioner
题意:500组数据,初始温度m,初始时间0,每个时间点可以选择温度上调1或者下调1,问能不能保证n(100)个人满意,即使得在t时温度在l和h之间。
思路:维护当前可能的温度区间即可,如果全过程都非空则可以。维护时从上一个人的区间转移到下一个人的区间,只需要通过二者时间差维护出可达到的最高和最低温度(一直升温或者降温),然后再对当前人所需要的区间取交集即可。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=105; 17 using namespace std; 18 int T,n,m; 19 int t[N],l[N],h[N]; 20 int main(){ 21 // freopen("in.txt","r",stdin); 22 rd(T); 23 while(T--){ 24 rd(n);rd(m); 25 for(int i=1;i<=n;i++)rd(t[i]),rd(l[i]),rd(h[i]); 26 int now_t=0,now_l=m,now_h=m; 27 bool flg=0; 28 for(int i=1;i<=n;i++){ 29 now_l=now_l-(t[i]-now_t); 30 now_h=now_h+(t[i]-now_t); 31 now_t=t[i]; 32 if(now_h < l[i]){flg=1;break;} 33 if(now_l > h[i]){flg=1;break;} 34 now_h=min(now_h,h[i]); 35 now_l=max(now_l,l[i]); 36 } 37 if(!flg)printf("YES "); 38 else printf("NO "); 39 } 40 return 0; 41 } 42 /**/
D. Shortest and Longest LIS
题意:给n-1(2e5)个'>'和'<',构造出一个n的排列使得两项之间满足上述关系,并且使得其最长上升子序列最大和最小,输出两个序列。
思路:首先可以看到有一个上界和下界,上界是所有的小于号都能连起来,下界是最长的小于号组。考虑一一构造,对于它的上界,其实应该是小于号数目+1,因为对于l~r这一段小于号,它对应的应该是排列中l~r+1的位置,而r+1这个位置是大于号,我们的最长上升子序列一定不会取r+1这个位置,因为取了它后面的小于号将无法连接上,除非它后面没有小于号了,而这种情况下我么可以让l~r和n这个位置连接起来而不是和r+1这个位置,所以上界就是所有<的位置和n这个位置组成上升子序列,只需要从左到右对这些位置依次填入1,2,3...即可,然后为了满足'>',我们可以倒着把没填的位置继续接着填入4,5,6...再考虑它的下界,应该是最长的'<'段+1,从后往前枚举'<'段,依次对l~r填入1,2,3...然后的操作和构造上界时相同。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=2e5+10; 17 using namespace std; 18 int T; 19 int n; 20 char s[N]; 21 int mx[N],mn[N]; 22 int main(){ 23 // freopen("in.txt","r",stdin); 24 rd(T); 25 while(T--){ 26 rd(n);scanf("%s",s); 27 memset(mx,0,sizeof(mx)); 28 memset(mn,0,sizeof(mn)); 29 int now=0; 30 for(int i=0;i<n-1;i++)if(s[i] == '<')mx[i+1]=++now; 31 mx[n]=++now;for(int i=n-1;i>=1;i--)if(!mx[i])mx[i]=++now; 32 int l=-1,r=-1;now=0; 33 for(int i=n-2;i>=0;i--){ 34 if(s[i] == '<' && r == -1)r=i+1; 35 if(s[i] == '<')l=i+1; 36 if((s[i] == '>' || i == 0) && l != -1){ 37 for(int j=l;j<=r;j++)mn[j]=++now; 38 l=-1;r=-1; 39 } 40 }now=n; 41 for(int i=1;i<=n;i++)if(!mn[i])mn[i]=now--; 42 for(int i=1;i<=n;i++)printf("%d ",mn[i]);puts(""); 43 for(int i=1;i<=n;i++)printf("%d ",mx[i]);puts(""); 44 } 45 return 0; 46 } 47 /**/
反思:先找出显然的上下界,然后证明这个上下界可以达到,然后进行构造。
E. 1-Trees and Queries
题意:n(1e5)个点的树,q(1e5)次询问,"假如xy连接,ab之间存不存在一条长度为k的路径",需要注意这里的路径可以经过重复的边或是点。
思路:首先,我们可以在一条边上反复横跳,所以如果只要我们可以找出一条长度小于等于k并且奇偶性和k相同的路径便可以找出一条长为k的路径。如果经过xy,我们可以发现ab之间的路径可以分为四部分,xy,xy所形成环的剩下部分,xa,yb。因为可以证明xayb和xbya是奇偶性相同的,不妨认为xayb是比较短的那一个。其他部分只要走了就必须原路返回,奇偶性不变,所以没有意义。我们还可以发现前两部分个数之和必须为奇数,不然没办法走出来。在我们确定了各部分的个数之后,我们可以换一种方式走完这些路径,先走完第一部分再走完第二部分,那么我们还可以发现,每一部分的个数都不能大于1,一旦大于1可以两个一组删除掉而不改变奇偶性,所以其实就是xy,xa,yb三部分,对这条路判断一下是否小于k且奇偶性相同即可。如果不经过xy直接判断最短路即可,所有路径奇偶性也必然是相同的,也是原路返回的原因。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=1e5+10; 17 using namespace std; 18 int n,q; 19 int fi[N],nxt[N<<1],to[N<<1],tot; 20 void link(int x,int y){nxt[++tot]=fi[x];fi[x]=tot;to[tot]=y;} 21 int dep[N],siz[N],son[N],top[N],fa[N]; 22 void dfs1(int x){ 23 siz[x]=1; 24 for(int i=fi[x];i;i=nxt[i]){ 25 if(dep[to[i]])continue; 26 dep[to[i]] = dep[x]+1;fa[to[i]]=x; 27 dfs1(to[i]);siz[x]+=siz[to[i]]; 28 if(siz[to[i]] > siz[son[x]])son[x]=to[i]; 29 } 30 } 31 void dfs2(int x,int y){ 32 top[x]=y;if(son[x])dfs2(son[x],y); 33 for(int i=fi[x];i;i=nxt[i])if(!top[to[i]])dfs2(to[i],to[i]); 34 } 35 int get_lc(int x,int y){ 36 while(top[x] != top[y]){ 37 if(dep[top[x]] < dep[top[y]])swap(x,y); 38 x=fa[top[x]]; 39 } 40 return dep[x] < dep[y] ? x : y; 41 } 42 int get_dis(int x,int y){ 43 return dep[x]+dep[y]-dep[get_lc(x,y)]*2; 44 } 45 int main(){ 46 // freopen("in.txt","r",stdin); 47 rd(n); 48 for(int i=1;i<n;i++){ 49 int x,y;rd(x);rd(y); 50 link(x,y);link(y,x); 51 } 52 dep[1]=1;dfs1(1);dfs2(1,1);rd(q); 53 for(int i=1;i<=q;i++){ 54 int x,y,a,b,k; 55 rd(x);rd(y);rd(a);rd(b);rd(k); 56 int dis1=get_dis(a,b);bool flg=0; 57 int dis2=min(get_dis(x,a)+get_dis(y,b)+1,get_dis(x,b)+get_dis(y,a)+1); 58 if(dis1 <= k && ((dis1&1) == (k&1)))flg=1; 59 if(dis2 <= k && ((dis2&1) == (k&1)))flg=1; 60 if(flg)printf("YES ");else printf("NO "); 61 } 62 return 0; 63 } 64 /**/
反思:大胆猜结论。题解真的想了很久,总觉得官方的不严谨,也可能是我没理解好,但我这个应该是绝对正确得了。考场肯定不要证明,直接猜。需要记住的可能就是树上两点间距离奇偶性不会改变吧。还有一个好的思路是走的哪些边确定之后可以换一种绝对可行的遍历方式,进而通过环的形成而抵消一些操作进而简化问题。
F. Animal Observation
题意:n(50)行m(2e4)列的矩阵,每个点有一个非负数,每行可以选一个长度为k(2e4)的区间,该行和其下面一行(如果有的话)的这个区间被覆盖,多次覆盖不重复计算权值,问权值之和最大是多少。
思路:不妨补上n+1行,全为0,这样更一致,f[i][j]表示第i行覆盖区间左端点为j时的最大值,从f[i]转移到f[i+1]并不容易,但是从f[i-1]转移到f[i]很容易,我们只需要记录f[i-1]前缀最大值,后缀最大值,就可以得到i行和i-1行区间不相交的情况下的最大值,相交的情况可以暴力枚举,这是f1的小数据,f2大数据可以利用线段树优化,具体维护的是f[i-1]对应位置减去重叠部分,我们在枚举j的过程中只需要维护单点修改和区间修改即可,需要注意从前面相交和从后面相交要分开处理,但是只用一颗树就可以维护了。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=55; 17 const int M=2e4+10; 18 using namespace std; 19 int n,m,k; 20 int a[N][M],s[N][M]; 21 int f[N][M]; 22 int mx_l[M],mx_r[M]; 23 int t_mx[M<<2],tag[M<<2]; 24 #define ls (o<<1) 25 #define rs (o<<1|1) 26 #define mid (l+r>>1) 27 void tagdown(int o){ 28 t_mx[ls]+=tag[o];tag[ls]+=tag[o]; 29 t_mx[rs]+=tag[o];tag[rs]+=tag[o]; 30 tag[o]=0; 31 } 32 void modify(int o,int l,int r,int x,int y){ 33 if(l == r)t_mx[o]=y; 34 else { 35 tagdown(o); 36 if(x <= mid)modify(ls,l,mid,x,y); 37 else modify(rs,mid+1,r,x,y); 38 t_mx[o]=max(t_mx[ls],t_mx[rs]); 39 } 40 } 41 void add(int o,int l,int r,int L,int R,int x){ 42 if(l >= L && r <= R)t_mx[o]+=x,tag[o]+=x; 43 else { 44 tagdown(o); 45 if(mid >= L)add(ls,l,mid,L,R,x); 46 if(mid < R)add(rs,mid+1,r,L,R,x); 47 t_mx[o]=max(t_mx[ls],t_mx[rs]); 48 } 49 } 50 int query(int o,int l,int r,int L,int R){ 51 if(l >= L && r <= R)return t_mx[o]; 52 else { 53 int ans=0;tagdown(o); 54 if(mid >= L)ans=max(ans,query(ls,l,mid,L,R)); 55 if(mid < R)ans=max(ans,query(rs,mid+1,r,L,R)); 56 return ans; 57 } 58 } 59 int main(){ 60 // freopen("in.txt","r",stdin); 61 rd(n);rd(m);rd(k); 62 for(int i=1;i<=n;i++) 63 for(int j=1;j<=m;j++) 64 rd(a[i][j]); 65 for(int i=1;i<=n+1;i++) 66 for(int j=1;j<=m;j++) 67 s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j]; 68 for(int i=1;i+k-1<=m;i++)f[1][i]=s[2][i+k-1]-s[2][i-1]; 69 for(int i=2;i<=n;i++){ 70 for(int j=1;j<=m;j++)mx_l[j]=max(mx_l[j-1],f[i-1][j]); 71 for(int j=m;j>=1;j--)mx_r[j]=max(mx_r[j+1],f[i-1][j]);//懒得卡出真正用得到的范围了 72 for(int j=1;j+k-1<=m;j++){ 73 int sum=s[i+1][j+k-1]-s[i-1][j+k-1]-s[i+1][j-1]+s[i-1][j-1]; 74 int sum2=s[i][j+k-1]-s[i-1][j+k-1]-s[i][j-1]+s[i-1][j-1]; 75 if(j > k)f[i][j]=max(f[i][j],mx_l[j-k]+sum); 76 modify(1,1,m,j,f[i-1][j]-sum2);//此处并不是f[i-2][j] 77 if(j-1)add(1,1,m,max(1,j-k),j-1,a[i][j-1]); 78 f[i][j]=max(f[i][j],query(1,1,m,max(1,j-k+1),j)+sum); 79 } 80 for(int j=m-k+1;j>=1;j--){ 81 int sum=s[i+1][j+k-1]-s[i-1][j+k-1]-s[i+1][j-1]+s[i-1][j-1]; 82 int sum2=s[i][j+k-1]-s[i-1][j+k-1]-s[i][j-1]+s[i-1][j-1]; 83 f[i][j]=max(f[i][j],mx_r[j+k]+sum); 84 modify(1,1,m,j,f[i-1][j]-sum2); 85 add(1,1,m,j+1,j+k-1,a[i][j+k]); 86 f[i][j]=max(f[i][j],query(1,1,m,j,j+k-1)+sum); 87 } 88 } 89 int ans=0;for(int i=1;i+k-1<=m;i++)ans=max(ans,f[n][i]);printf("%d ",ans); 90 return 0; 91 } 92 /**/
反思:需要注意dp的转移,很多时候都会在i-1转移到i和i转移到i+1之间做选择,其中一个往往比另一个更优,甚至可以用数据结构优化。