Problem1 Preokret
第一题一定不是什么难题。
第一个问题在读入的时候判断当前时间是不是在1440及以前就行
第二个问题考虑离线处理,由于每个时刻只能最多发生1个事件那么就弄个桶记录每一个事件就行了。
水过T1。
Code:
# include <bits/stdc++.h> using namespace std; const int N=2881; int a[N]; int A,B,T,rec1,rec2; template <typename T>inline void read(T &x) { int w=0,X=0; char c=0; while (c<'0'||c>'9') w|=c=='-',c=getchar(); while (c>='0'&&c<='9') X=(X<<1)+(X<<3)+(c^48),c=getchar(); x=w?-X:X; } int main() { // freopen("Preokret.in","r",stdin); // freopen("Preokret.out","w",stdout); memset(a,-1,sizeof(a)); read(A); for (int i=1;i<=A;i++) read(T),a[T]=0; read(B); for (int i=1;i<=B;i++) read(T),a[T]=1; int Ans1=0,Ans2=0,flag=-1,s1=0,s2=0; for (int i=1;i<=2880;i++) { if (a[i]==-1) continue; if (a[i]==0) s1++; else s2++; if (i<=1440) Ans1++; if (flag==-1) { flag=s1<s2; continue;} if (s1>s2&&flag==1) flag=0,Ans2++; else if (s1<s2&&flag==0) flag=1,Ans2++; } cout<<Ans1<<' '<<Ans2<<' '; return 0; }
Problem2 Kocka
这个题对于100%的数据一定不是O(n2)的构造,而是数学的判断。
我们可以简单考虑四种情况,我们记四个数组分别是left[],right[],up[],down[],
分别考虑四个数组元素值是x的时候代表什么含义,
以left[i]为例,left[i]=-1的时候表示第i行没有放东西,那么对应的right[i]必须也是-1否则构造不合法,而对于up[]和down[]无影响。
left[i]=x(x≠-1时),那么意味着在(i,x+1)这个位置必须存在一个障碍物,那么从上面看下来可以看到的不能大于i-1吧,下面看上来不能超过n-i吧,右边看过来不能超过n-x-1吧。
对于 right[]和up[]和down[]同理。
这里需要注意在C++中up什么的可能是保留字,我Define了一下。
Code
# include <bits/stdc++.h> # define fp(i,s,t) for (int i=s;i<=t;i++) # define left Left # define right Right # define up Up # define down Down using namespace std; const int N=1e5+10; int left[N],right[N],up[N],down[N],n; inline void read(int &x) { int w=0,X=0; char c=0; while (c<'0'||c>'9') w|=c=='-',c=getchar(); while (c>='0'&&c<='9') X=(X<<1)+(X<<3)+(c^48),c=getchar(); x=w?-X:X; } bool work() { fp(i,1,n) { if (left[i]==-1) { if (right[i]==-1) continue; return 0; } if (up[left[i]+1]==-1||up[left[i]+1]>i-1) return 0; if (down[left[i]+1]==-1||down[left[i]+1]>n-i) return 0; if (right[i]==-1||right[i]>n-left[i]-1) return 0; } fp(i,1,n) { if (up[i]==-1) { if (up[i]==-1) continue; return 0; } if (left[up[i]+1]==-1||left[up[i]+1]>i-1) return 0; if (right[up[i]+1]==-1||right[up[i]+1]>n-i) return 0; if (down[i]==-1||down[i]>n-up[i]-1) return 0; } fp(i,1,n) { if (right[i]==-1) { if (left[i]==-1) continue; return 0; } if (up[n-right[i]]==-1||up[n-right[i]]>i-1) return 0; if (down[n-right[i]]==-1||down[n-right[i]]>n-i) return 0; if (left[i]==-1||left[i]>n-right[i]-1) return 0; } fp(i,1,n) { if (down[i]==-1) { if (up[i]==-1) continue; return 0; } if (left[n-down[i]]==-1||left[n-down[i]]>i-1) return 0; if (right[n-down[i]]==-1||right[n-down[i]]>n-i) return 0; if (up[i]==-1||up[i]>n-down[i]-1) return 0; } return 1; } int main() { // freopen("Kocka.in","r",stdin); // freopen("Kocka.out","w",stdout); read(n); fp(i,1,n) read(left[i]); fp(i,1,n) read(right[i]); fp(i,1,n) read(up[i]); fp(i,1,n) read(down[i]); puts(work()?"DA":"NE"); return 0; }
Problem3 Deblo
这个题是树上路径计数,初步搞搞O(n2log2 n)暴力还是搞得来的。
但是书上路径统计想到了淀粉质点分治暴力统计路径。
这里需要注意点分治的一般步骤:
每次在子树里面选定一个重心,考虑一条过重心的路径,统计这条边上的异或值。
这里用到异或的一个性质x xor y xor y=x,考虑以子树的根(不一定是重心)dfs下去找到根的xor前缀和
统计每个点的xor,那么就把01塞到桶里面就行了。
注意这个时候由于每一条路径一定过重心所以先把重心加入,其他的前缀和先xor一个重心,这样可以把重心的影响减去。
尤其注意先找这个点和其他点之间xor值然后再更新桶里面的值。
重心打标及时啊!一塞入就打标。
一开始先找重心可以快一点。。。
具体的可以看我的点分治blog例题1:https://www.cnblogs.com/ljc20020730/p/10347198.html
# include <bits/stdc++.h> # define LL long long # define fp(i,s,t) for (int i=s;i<=t;i++) using namespace std; const int N=1e5+10; const int MAXBIT=22; bool used[N]; int size[N],val[N],cnt[2][MAXBIT],n; struct rec{int pre,to;}a[2*N]; int tot,head[N]; template <typename T>inline void read(T& t) { char c=getchar();t=0; while(c<'0'||c>'9')c=getchar(); while(c>='0'&&c<='9')t=(t<<1)+(t<<3)+(c^48),c=getchar(); } template <typename T,typename... Args> inline void read(T& t, Args&... args) { read(t);read(args...); } void write(LL x) { if (x>9) write(x/10); putchar('0'+x%10); } void writeln(LL x) { write(x);putchar(' '); } void adde(int u,int v) { a[++tot].pre=head[u]; a[tot].to=v; head[u]=tot; } int Get_Root(int u,int fath,int S) { int pos=0; for (int i=head[u];i;i=a[i].pre) { int v=a[i].to; if (v==fath||used[v]) continue; if (size[v]>size[pos]) pos=v; } if (pos!=0&&size[pos]>=S/2) return Get_Root(pos,u,S); return u; } void Get_Dist(int u,int fath,int num) { num^=val[u]; size[u]=1; fp(i,0,MAXBIT-1) cnt[(num & (1<<i))>0][i]++; for (int i=head[u];i;i=a[i].pre){ int v=a[i].to; if (v==fath||used[v]) continue; Get_Dist(v,u,num); size[u]+=size[v]; } } LL ans=0; void Get_Ans(int u,int fath,int num) { num^=val[u]; fp(i,0,MAXBIT-1) ans+=(LL) (1<<i)*cnt[(num & (1<<i))==0][i]; for (int i=head[u];i;i=a[i].pre){ int v=a[i].to; if (v==fath||used[v]) continue; Get_Ans(v,u,num); } } void solve(int u,int fath) { int root=Get_Root(u,fath,size[u]); used[root]=1; memset(cnt,0,sizeof(cnt)); fp(i,0,MAXBIT-1) cnt[(val[root] & (1<<i))>0][i]++; ans+=(LL)val[root]; for (int i=head[root];i;i=a[i].pre) { int v=a[i].to; if (used[v]) continue; Get_Ans(v,0,0); Get_Dist(v,0,val[root]); } for (int i=head[root];i;i=a[i].pre) { int v=a[i].to; if (used[v]) continue; solve(v,u); } } int main() { read(n); fp(i,1,n) read(val[i]); int u,v; fp(i,1,n-1) read(u,v),adde(u,v),adde(v,u); int rt=Get_Root(1,0,n); Get_Dist(rt,0,0); solve(rt,0); writeln(ans); return 0; }
Problem4 Maja
数据加强的三个点其实就是把大多数标程卡掉的点。
就是K不是偶数而是奇数的时候显然是不存在合法的一条路径的。
这点原题中没有说需要判断。(但是神奇的是没有卡WTF)
这道题显然K这么大不是搜索而是DP,
搜索的复杂度O(4K)太大了吧!!! 而且每个点不止重复走1次。
考虑一条性质:假设从原点(A,B)走到(i,j)恰好走了K/2步然后必然会选择走回去是最优的吧。
这个显然。
然后考虑全局的最优,答案一定是一个循环组成的,就是不停地在某一条路上来回走。
这个也是显然的吧。
那么有余数怎么办?可以证明贪心的选取中点四周的一个Ci,j最大的来回走完余数就是这个点最优的。
这个可以反证? 简单yy一下如果不这样选取那么你还不如走到你最终的中点继续循环呢!
然后就是一个DP+贪心的神仙题目了。
先K/=2,只考虑去,回来其实近似乘以2就行。(其实并不是乘以2)
设f[i][j][r]指的是从(A,B)走到(i,j)经过(i,j)有r次的最优解,那么显然是从四个方向走来的吧。
转移是f[i][j][r]=Max{f[x][y][r-1]}+c[i][j](x,y指的是(i,j)四个相邻点)
回去就是f[i][j][r]+Max{f[x][y][r-1]},那是Ci,j少记一次。
然后剩余的步数(只记去,此时K已经除以2)就是K-r,余数走的步数就是
(K-r)*(Max{c[x][y]}+c[i][j])
然后每次到(i,j)r次计算一下答案。
那个r一维可以滚存,r的取值范围是[0,Min(k,n*m)]
Code:
# include<bits/stdc++.h> # define fp(i,s,t) for (int i=s;i<=t;i++) # define inf (0x7f7f7f7f7fll) # define int long long using namespace std; const int N=105; int f[N][N][2],c[N][N]; int n,m,A,B,K,ans; int Get_Max(int a,int b,int c,int d) { if (b>a) a=b; if (c>a) a=c; if (d>a) a=d; return a; } template <typename T>inline void read(T& t) { char c=getchar();t=0; while(c<'0'||c>'9')c=getchar(); while(c>='0'&&c<='9')t=(t<<1)+(t<<3)+(c^48),c=getchar(); } template <typename T,typename... Args> inline void read(T& t, Args&... args) { read(t);read(args...); } void write(int x) { if (x>9) write(x/10); putchar('0'+x%10); } void writeln(int x) { write(x);putchar(' '); } signed main() { read(n,m,A,B,K); if (K&1) { puts("0");exit(0);} K=K>>1; fp(i,0,n+1) fp(j,0,m+1) c[i][j]=f[i][j][0]=f[i][j][1]=-inf; fp(i,1,n) fp(j,1,m) read(c[i][j]); f[A][B][0]=0; ans=-inf; fp (r,1,min(K,n*m)) { int now=r&1; int pst=now^1; fp(i,1,n) fp(j,1,m) { int temp=Get_Max(f[i-1][j][pst],f[i+1][j][pst],f[i][j-1][pst],f[i][j+1][pst]); f[i][j][now]=max(temp+c[i][j],-inf); if (f[i][j][now]<0) continue; int Whole_Dist=f[i][j][now]+temp; Whole_Dist+=(K-r)*(c[i][j]+Get_Max(c[i-1][j],c[i+1][j],c[i][j-1],c[i][j+1])); ans=max(ans,Whole_Dist); } } writeln(ans); return 0; }
Problem5 Sunčanje
一道暴力的神仙T5,一看到数据范围就不想暴力的。
但是算算暴力的期望还是值得的。
先按照矩形右下角的x坐标排序,然后枚举每一个长方形,i,看看前面的长方形j是不是包含在i里面的,
被包含的必要条件是a[i].x-a[i].len<a[j].x对吧,不满足必要条件的那么前面也不满足必要条件,直接跳掉。
然后花四个图看看判包含。
(a[i].y>a[j].y&&a[i].y<a[j].y+a[j].wid) ||(a[i].y+a[i].wid>a[j].y&&a[i].y+a[i].wid<a[j].y+a[j].wid) ||(a[j].y>a[i].y&&a[j].y<a[i].y+a[i].wid) ||(a[j].y+a[j].wid>a[i].y&&a[j].y+a[j].wid<a[i].y+a[i].wid)
更新的时候注意看看谁在谁的下面(一定是初始标号小的放在下面!)
然后瞎加点优化就可以用pascal过4000ms时限了(而C++不行)
然后我就把第19个点的时限改成了6000ms,于是pascal和C++都过了
如果C++代码在XOJ上过不了的话那么加一行
# pragma G++ optimize(3)
Code C++
# pragma G++ optimize(3)
# include <bits/stdc++.h> # define fp(i,s,t) for (int i=s;i<=t;i++) using namespace std; const int N=1e5+10; struct rec{ int x,y,len,wid,id; }a[N]; int n; bool ans[N]; inline int read() { int X=0,w=0; char c=0; while(c<'0'||c>'9') {w|=c=='-';c=getchar();} while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar(); return w?-X:X; } inline void qsort(int l,int r) { if (l==r) return; int t=rand()%(r-l)+l; swap(a[t],a[l]); rec v=a[l]; int i=l,j=r; while (i<j) { while (i<j&&a[j].x>v.x) j--; if (i<j) {a[i]=a[j];i++;} else break; while (i<j&&a[i].x<v.x) i++; if (i<j) {a[j]=a[i];j--;} else break; } a[i]=v; if (l<j) qsort(l,j-1); if (i<r) qsort(i+1,r); } signed main() { srand(time(NULL)*100007); n=read(); fp(i,1,n) { a[i].x=read();a[i].y=read();a[i].len=read();a[i].wid=read(); a[i].x+=a[i].len; a[i].id=i; } qsort(1,n); fp(i,2,n) { int j=i-1; while (j>=1&&(a[i].x-a[i].len<a[j].x)) { if ((a[i].y>a[j].y&&a[i].y<a[j].y+a[j].wid) ||(a[i].y+a[i].wid>a[j].y&&a[i].y+a[i].wid<a[j].y+a[j].wid) ||(a[j].y>a[i].y&&a[j].y<a[i].y+a[i].wid) ||(a[j].y+a[j].wid>a[i].y&&a[j].y+a[j].wid<a[i].y+a[i].wid)) { if (a[i].id<a[j].id) ans[a[i].id]=1; else ans[a[j].id]=1; } j--; } } fp(i,1,n) if (ans[i]) putchar('N'),putchar('E'),putchar(' '); else putchar('D'),putchar('A'),putchar(' '); return 0; }
Code Pascal
VAR n,i,j:int64; x,y,len,wid,poza,raspuns:array[1..100001] of int64; function Poz(a1,b1 : int64) : int64; VAR aux,piv:int64; Begin piv:=x[a1]; while (a1 < b1) do Begin if (x[a1] > x[b1]) then Begin aux:=x[a1]; x[a1]:=x[b1]; x[b1]:=aux; aux:=y[a1]; y[a1]:=y[b1]; y[b1]:=aux; aux:=len[a1]; len[a1]:=len[b1]; len[b1]:=aux; aux:=wid[a1]; wid[a1]:=wid[b1]; wid[b1]:=aux; aux:=poza[a1]; poza[a1]:=poza[b1]; poza[b1]:=aux; end; if (piv = x[a1]) then b1:=b1 - 1 else a1:=a1 + 1; end; Poz:=a1; end; procedure Quick(a1,b1 : int64); VAR k:int64; Begin if (a1 < b1) then Begin k:=Poz(a1,b1); Quick(a1,k - 1); Quick(k + 1,b1); end; end; Begin Readln(n); for i:=1 to n do Begin Readln(x[i],y[i],len[i],wid[i]); x[i]:=x[i] + len[i]; poza[i]:=i; end; Quick(1,n); raspuns[1]:=0; for i:=2 to n do Begin j:=i - 1; while (j > 0) and (x[i] - len[i] < x[j]) do Begin if (((y[i] > y[j]) and (y[i] < y[j] + wid[j])) or ((y[i] + wid[i] > y[j]) and (y[i] + wid[i] < y[j] + wid[j]))) or (((y[j] > y[i]) and (y[j] < y[i] + wid[i])) or ((y[j] + wid[j] > y[i]) and (y[j] + wid[j] < y[i] + wid[i]))) then Begin if (poza[i] < poza[j]) then raspuns[poza[i]]:=1 else raspuns[poza[j]]:=1; end; j:=j - 1; end; end; for i:=1 to n do if (raspuns[i] = 1) then Writeln('NE') else Writeln('DA'); END.