T1 Blue
贪心,每次跳得时候跳能跳到的最远的地方,跳过的就把他设为0,每次二分找到位置,一直跳就行,如果能跳到的位置就是当前位置或比当前位置还小(数组里现在呆着的这一块石头,二分得到的就是当前位置,-1就比当前位置小了。但由于0的影响,while回退很慢,所以改用支持earse操作的set,可以水过他(这是T40和AC的区别!!!)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
using namespace std;
int T,n,m,d,l;
set<int>st;
set<int>::iterator it;
int read()
{
int aa=0,bb=1;char cc=getchar();
while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();}
while(cc<='9'&&cc>='0'){aa=aa*10+cc-'0';cc=getchar();}
return aa*bb;
}
int main()
{
T=read();
while(T--){
st.clear();
n=read();m=read();d=read(),l=read();
bool flag=0,flag1=0;
for(int i=1;i<=n;i++){
st.insert(read());
}
st.insert(l);st.insert(0);
if(d==l){
puts("Excited");
continue;
}
for(int i=1;i<=m;i++){
int pos=0;
while(pos<l){
if(pos+d>=l) break;
it=st.upper_bound(pos+d);
if(*(--it)<=pos){
flag=1;
break;
}
pos=*it;
st.erase(it);
}
if(flag){
printf("%d\n",i-1);
flag1=1;
break;
}
}
if(flag1) continue;
puts("Excited");
}
return 0;
}
T2 Weed
树袋熊学长的课件里讲过,然而我并不会。
又是一道神奇的线段树,用一个神奇的cal函数,我们就可以愉快的拿到100分的好成绩!
以时间为下标,建一棵线段树,树中维护4个值sum,del,cnt,las
sum:当前区间内所有操作添 删之后剩下金克拉的总和 ans=t[1].sum
del:当前区间还要向前(当前区间的前一个区间)删除多少层
cnt:当前区间被自己区间删完后还剩多少层
las:左儿子被右儿子用del删除后还剩多少(只对左儿子维护)
cal函数的作用就是计算las的值,update的时候维护x信息时,左儿子会被右儿子删除,为了维护区间信息的正确性,需要用右儿子的del删除左儿子的一些值,删除等操作我们只是保存在区间信息里,而不对下面的节点做修改(因为这是区间信息)。左儿子的cnt和右儿子的del有下面几种关系,分别处理即可
1.l.cnt<r.del 左儿子不够删,那就直接当作左儿子不存在,计算剩下的值得到x的sum值
2.r.del==0 右儿子中不存在删除操作,直接合并
3.l.r.cnt>r.del 左儿子的右儿子就够右儿子删了,return l.las+cal(l.r,r.del)
4.l.r.cnt<r.del 左儿子的右儿子不够右儿子不够删,那就继续去左儿子的左儿子里删 return cal(l.l,r.del-l.r.cnt+l.r.del) 记得加上左儿子的右儿子对左儿子的左儿子的del
5.l.r.cnt==r.del 左儿子的右儿子刚好够删,直接return l.las
修改操作实际上就是线段树的单点修改操作,改动一个值,其他区间的信息会被update上去,最后t[1].sum即为答案。
/*sum:区间内删完后的总和
del:当前区间要往前删多少个
cnt:当前区间删完后还有多少个加的操作
las:左儿子被右儿子删完后的总和*/
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
struct node
{
int l,r,sum,del,cnt,las;
}t[800100];
int n,m,opt,v;
int read()
{
int aa=0,bb=1;char cc=getchar();
while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();}
while(cc<='9'&&cc>='0'){aa=aa*10+cc-'0';cc=getchar();}
return aa*bb;
}
int cal(int x,int num)
{
if(t[x*2+1].cnt>num) return t[x*2].las+cal(x*2+1,num);
if(t[x*2+1].cnt<num) return cal(x*2,num-t[x*2+1].cnt+t[x*2+1].del);
if(t[x*2+1].cnt==num) return t[x*2].las;
}
void update(int x)
{
if(!t[x*2+1].del){
t[x].sum=t[x*2].sum+t[x*2+1].sum;
t[x].del=t[x*2].del;
t[x].cnt=t[x*2].cnt+t[x*2+1].cnt;
t[x*2].las=t[x*2].sum;
return;
}
if(t[x*2].cnt<=t[x*2+1].del){
t[x].sum=t[x*2+1].sum;
t[x].del=t[x*2].del+t[x*2+1].del-t[x*2].cnt;
t[x].cnt=t[x*2+1].cnt;
t[x*2].las=0;
return;
}
t[x*2].las=cal(x*2,t[x*2+1].del);
t[x].sum=t[x*2].las+t[x*2+1].sum;
t[x].del=t[x*2].del;
t[x].cnt=t[x*2+1].cnt+t[x*2].cnt-t[x*2+1].del;
}
void build(int x,int l,int r)
{
t[x].l=l;t[x].r=r;
if(l==r){
opt=read();
if(opt==0){
t[x].sum=read();
t[x].del=0;
t[x].cnt=1;
t[x].las=0;
}
else{
t[x].sum=0;
t[x].del=read();
t[x].cnt=0;
t[x].las=0;
}
return;
}
int mid=(l+r)>>1;
build(x*2,l,mid);
build(x*2+1,mid+1,r);
update(x);
}
void change(int x,int pos)
{
if(t[x].l==t[x].r&&t[x].l==pos){
if(opt==0){
t[x].sum=v;
t[x].del=0;
t[x].cnt=1;
t[x].las=0;
}
else{
t[x].sum=0;
t[x].del=v;
t[x].cnt=0;
t[x].las=0;
}
return;
}
int mid=(t[x].l+t[x].r)>>1;
if(pos<=mid) change(x*2,pos);
else change(x*2+1,pos);
update(x);
}
int main()
{
n=read();m=read();
build(1,1,n);
for(int i=1,j;i<=m;i++){
j=read();opt=read();v=read();
change(1,j);
printf("%d\n",t[1].sum);
}
return 0;
}
T3 Drink
一圈一圈的先存下来,然后转后应在的地方“铺”上去,用队列存T30,用数组存并且用char类型存储表格(只有1~9)可以到T60,调整一下循环顺序T80,register删掉,for改while可以卡到T90,最后把快读稍改,就A了
就一句话,疯狂卡常可以A。
然而正解并不长这样。。。具体长啥样我也不知道
以下是正解:
Drink:
看惯了罗马音的小朋友们都会知道r发l的音,题目名:D Link.
每次修改都会改变O( N ^ 2 )个位置的值,二维平面上没有一个确定的正方向,因此也无法使用传统数据结构。
什么东西的变化量是O( N )级别的呢?
如果把每个点都看作一个人,他的头面向某个方向。分别记录这个人的方向上的前后左右分别是谁,那么每次旋转改变的只是正方形边缘上的值,以及所有点的方向。
这样来看至少我们发现了变化量为O( N )级别的东西啦(虽然方向的该变量还是O( N ^ 2 ))。
注意到,我们并不需要真的知道每个点的方向,我们只需要正确地维护前后左右四个值即可。因为每个点的方向是可以由已知方向的相邻点算出来的!
每个点确实是有方向的,但是我们不用纪录也不用直接更改它,每次只要正确修改边界上的值,整个矩形的方向就会自动改变。
解决啦!
复杂度O( Q * N ), 常数稍大。
#include<iostream>
#include<cstdio>
const int L=1<<20|1;
char buffer[L],*S,*T;
#define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++)
using namespace std;
char a[2010][2010],c,b[8010];
int read()
{
int aa=0,bb=1;char cc=getchar();
while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();}
while(cc<='9'&&cc>='0'){aa=(aa<<3)+(aa<<1)+(cc^48);cc=getchar();}
return aa*bb;
}
int main()
{
int n,m,qq,num;
n=read(),m=read(),qq=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
c=getchar();
while(c<'0'||c>'9') c=getchar();
a[i][j]=c;
}
int x,y,l,i=1,j;
while(i<=qq){
x=read();y=read();l=read();
while(l>=2){
num=0;
j=0;while(j<l) b[++num]=a[x][y+j],++j;
j=1;while(j<l) b[++num]=a[x+j][y+l-1],++j;
j=l-2;while(j>=0) b[++num]=a[x+l-1][y+j],--j;
j=l-2;while(j>=1) b[++num]=a[x+j][y],--j;
j=l-2;while(j>=1) a[x][y+j]=b[num--],--j;
j=0;while(j<=l-2) a[x+j][y]=b[num--],++j;
j=0;while(j<=l-2) a[x+l-1][y+j]=b[num--],++j;
j=l-1;while(j>=0) a[x+j][y+l-1]=b[num--],--j;
x++,y++;l-=2;
}
i++;
}
i=1;
while(i<=n){
j=1;
while(j<=m){
putchar(a[i][j]),putchar(' ');
j++;
}
puts("");
i++;
}
return 0;
}
总之这道题的测试点还是很水的,T1T3暴力都可以水过,但样例给的太灭绝人性,把电脑卡崩3 4次。
来源:https://www.cnblogs.com/jrf123/p/11333106.html
