20190729考试反思
又是一场扔分的考试,T1暴力没调出来,T2没看到40分的点自己会,手扔40分。极限得分85+70+95=250然而实际25+30+95=150还是太菜了,现在才把T2改完。
T1就是一个辣鸡。。。。额它就是个暴力,首先这道题没有思维含量。。但我还是很满意自己在考场上的挽救大脑短路的操作的,其实一开始我是没有发现矩形内部可以直接算,也就是说别人一下想到的东西我大脑间歇抽搐没想到,然鹅我通过一系列的分析从一些边缘信息得到了这个,首先坐标1e9就是刚会打for循环都知道这不能存矩阵,然后只能看输入什么存什么,那么它为什么是一个矩形一个矩形给,而且为什么这个第一个5分它只给一个矩形,然后通过想这5分怎么打的时候发现了矩形中间是可以直接算的,矩形之间$n^{2}$枚举算。然后就结束了,这是一个非常神奇的复杂度,没有啥正确证明,本人只能口胡一个:理论上来说卡它的极限数据是每个矩形周围都有n个矩形能形成氢键,然而yy一下发现这是不可能的,也就是说一个矩形只能和有限个矩形形成氢键,而这有限个又很小,就算一个矩形周围很多键,那一定是这个矩形很大旁边的很小,那些小的周围的键就会少很多,所以均摊下来很小。。。应该是这样吧
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 const int X=1e9; 6 int rd() 7 { 8 int s=0,w=1; 9 char cc=getchar(); 10 while(cc<'0'||cc>'9') {if(cc=='-') w=-1;cc=getchar();} 11 while(cc>='0'&&cc<='9') s=(s<<3)+(s<<1)+cc-'0',cc=getchar(); 12 return s*w; 13 } 14 struct mat{int xa,xb,ya,yb;}m[100020]; 15 bool cmp1(mat a,mat b) 16 { 17 if(a.xa==b.xa) return a.ya<b.ya; 18 return a.xa<b.xa; 19 } 20 signed main() 21 { 22 int n=rd(); 23 if(n==1) 24 { 25 int xa=rd(),ya=rd(),xb=rd(),yb=rd(); 26 printf("%lld\n",1ll*(xb-xa)*(yb-ya)*2); 27 return 0; 28 } 29 long long ans=0; 30 for(int i=1;i<=n;i++) 31 { 32 m[i].xa=rd(),m[i].ya=rd(),m[i].xb=rd(),m[i].yb=rd(); 33 ans+=1ll*(m[i].xb-m[i].xa)*(m[i].yb-m[i].ya)*2; 34 } 35 sort(m+1,m+n+1,cmp1); 36 for(int i=1;i<=n;i++) 37 { 38 for(int j=i+1;j<=n;j++) 39 { 40 if(m[j].xa>m[i].xb+1)break; 41 else if(m[j].xa==m[i].xb+1) 42 { 43 int a=m[i].ya,b=m[i].yb,c=m[j].ya,d=m[j].yb; 44 int mu=min(a,c),mmu=max(a,c),md=max(b,d),mmd=min(b,d); 45 if(mmu>mmd+1){continue;} 46 else if(mmu==mmd+1){ans++;continue;} 47 ans+=(md-mu)<<1; 48 ans-=(mmu-mu)<<1;ans-=(md-mmd)<<1; 49 if(mu!=mmu)ans++; 50 if(md!=mmd)ans++; 51 } 52 else if(m[j].xa<=m[i].xb) 53 { 54 if(m[j].ya!=m[i].yb+1&&m[i].ya!=m[j].yb+1) continue; 55 int a=m[i].xa,b=m[i].xb,c=m[j].xa,d=m[j].xb; 56 int mu=min(a,c),mmu=max(a,c),md=max(b,d),mmd=min(b,d); 57 //if(mmu>mmd+1){continue;} 58 //else if(mmu==mmd+1){ans++;continue;} 59 ans+=(md-mu)*2; 60 ans-=(mmu-mu)*2;ans-=(md-mmd)*2; 61 if(mu!=mmu)ans++; 62 if(md!=mmd)ans++; 63 } 64 } 65 } 66 printf("%lld\n",ans); 67 } 68 /* 69 g++ 1.cpp -o 1 70 ./1 71 10 72 1 8 8 9 73 0 3 10 7 74 0 0 7 0 75 0 2 9 2 76 4 10 8 10 77 10 0 10 2 78 0 10 0 10 79 8 0 9 1 80 0 8 0 9 81 9 8 10 8 82 */
T2神仙题,这个题是之前一个题的加强版,每次对一个点到根路径上的所有点放一种颜色的球,每个点上放球的数量都有一个ki限制,首先辣鸡papa没看出来有8个k=1e5的点,这是说每个点没限制,然后手动扔分。。。。以后要好好看每个部分分。那么我们先说正解,这个题我一开始的思路就是,树上差分每点维护权值线段树。然而这没法进行限制,再仔细想一下的话我会发现对于一个权值线段树我是没有办法进行时间统计的,那么我就想到了用时间为下标,那这样就没法统计颜色个数,然后就在矛盾中迷失了自我。。。。。最后看到了题解,对于每个点,我维护一个线段树,计算每个球的贡献(如果之前出现过就是0,否则为1),和总球数,然后我就可以二分像求区间k大值一样求出球数为ki时的总贡献和。
然后我就骂出了声,这**不是暴力么。。。听到Lrefrain神说启发式合并能保证这个复杂度是log,然后就快乐的AC了,于是学习了一下比较神奇的启发式合并,发现和树链剖分的复杂度保障及其相似。我们先看这道题暴力怎么打,比如说我要维护每个点的操作的一个vector,这个东西相当于一个差分完的vector,代表某个节点比子节点多那些操作,那么我们把子树的vector全部合并,这才是这个节点的所有操作,然后把这个vector塞进线段树,统计一下答案。时间耗在了子树vector的合并上,要是每次都清vector然后每次重新搜子树会T,所有vector保留下来会M。。。
启发式合并就是让它其它儿子合并到重儿子上,重儿子不变,即:我每次dfs操作时,分情况,如果先搜当前的轻儿子,统计轻儿子答案,并清掉轻儿子用的线段树,然后递归重儿子,这次递归不清信息,一会再次递归所有轻儿子,把轻儿子的vector塞到x中,并更新当前节点线段树,之所以不能把两次轻儿子合并就是因为,如果合并那么要么把重儿子信息一起清了,要么一起留下,没法达到目的。这样我相当于保留一部分信息,临时处理一部分信息,让他既不M,也不T,就像好多数据结构干的事。我们通过重儿子划分让他保留的和临时处理达到一个最折中的方案。时间复杂度可以达到nlog,本人好像只会口胡证明,粘一发L神的题解
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdio> 4 #include<vector> 5 #include<map> 6 #define int long long 7 using namespace std; 8 const int N=1000020,M=1000020; 9 int k[N],fr[N*2],fa[N],rt[N],size[N],ans[N],check[N],son[N],tt,tot,cnt,n,m; 10 struct node{int fr,to,pr,f;}mo[N*2]; 11 struct flag{int t,c;}; 12 struct tree{int l,r,f,w,c;}tr[N*4]; 13 map<int,int> p; 14 vector<flag> v[N]; 15 int rd() 16 { 17 int s=0,w=1; 18 char cc=getchar(); 19 while(cc<'0'||cc>'9') {if(cc=='-') w=-1;cc=getchar();} 20 while(cc>='0'&&cc<='9') s=(s<<3)+(s<<1)+cc-'0',cc=getchar(); 21 return s*w; 22 } 23 void add(int x,int y) 24 { 25 mo[++tt].fr=x; 26 mo[tt].to=y; 27 mo[tt].pr=fr[x]; 28 fr[x]=tt; 29 } 30 inline void clear(int k) 31 { 32 tr[k].w=tr[k].c=0; 33 tr[k].f=1; 34 } 35 void down(int k) 36 { 37 tr[k].f=0; 38 clear(k<<1);clear(k<<1|1); 39 } 40 void build(int k,int l,int r) 41 { 42 tr[k].l=l,tr[k].r=r; 43 if(l==r) 44 { 45 tr[k].w=0; 46 return ; 47 } 48 int mid=l+r>>1; 49 build(k<<1,l,mid);build(k<<1|1,mid+1,r); 50 tr[k].w=tr[k<<1].w+tr[k<<1|1].w; 51 } 52 void cut(int k,int id) 53 { 54 int l=tr[k].l,r=tr[k].r,mid=l+r>>1; 55 if(l==r) 56 { 57 tr[k].c=0; 58 return ; 59 } 60 if(tr[k].f) down(k); 61 if(id<=mid) cut(k<<1,id); 62 else cut(k<<1|1,id); 63 tr[k].c=tr[k<<1].c+tr[k<<1|1].c; 64 } 65 void addw(int k,int id) 66 { 67 int l=tr[k].l,r=tr[k].r,mid=l+r>>1; 68 if(l==r) 69 { 70 tr[k].w++; 71 return; 72 } 73 if(tr[k].f) down(k); 74 if(id<=mid) addw(k<<1,id); 75 else addw(k<<1|1,id); 76 tr[k].w=tr[k<<1].w+tr[k<<1|1].w; 77 } 78 void addc(int k,int id) 79 { 80 int l=tr[k].l,r=tr[k].r,mid=l+r>>1; 81 if(l==r) 82 { 83 tr[k].c=1; 84 return ; 85 } 86 if(tr[k].f) down(k); 87 if(id<=mid) addc(k<<1,id); 88 else addc(k<<1|1,id); 89 tr[k].c=tr[k<<1].c+tr[k<<1|1].c; 90 } 91 int find(int k,int w) 92 { 93 if(w<=0) return 0; 94 int l=tr[k].l,r=tr[k].r,mid=l+r>>1; 95 if(l==r) return tr[k].c; 96 if(tr[k].f) down(k); 97 if(w<=tr[k<<1].w) return find(k<<1,w); 98 else return tr[k<<1].c+find(k<<1|1,w-tr[k<<1].w); 99 } 100 void dfs1(int x) 101 { 102 size[x]=1; 103 for(int i=fr[x];i;i=mo[i].pr) 104 { 105 int to=mo[i].to; 106 if(to==fa[x]) continue; 107 fa[to]=x; 108 dfs1(to); 109 size[x]+=size[to]; 110 if(size[to]>size[son[x]]) son[x]=to; 111 } 112 } 113 void pushvector(int x,int to) 114 { 115 for(int i=0;i<v[to].size();i++) 116 v[x].push_back(v[to][i]); 117 v[to].clear(); 118 } 119 void pushtree(int x) 120 { 121 for(int i=0;i<v[x].size();i++) 122 { 123 int t=v[x][i].t,c=v[x][i].c; 124 addw(1,t); 125 if(!check[c]) addc(1,t),check[c]=t; 126 else if(check[c]>t) 127 { 128 cut(1,check[c]); 129 addc(1,t); 130 check[c]=t; 131 } 132 } 133 } 134 void dfs2(int x,int op) 135 { 136 for(int i=fr[x];i;i=mo[i].pr) 137 { 138 int to=mo[i].to; 139 if(to==fa[x]||to==son[x]) continue; 140 dfs2(to,0); 141 } 142 if(son[x]) dfs2(son[x],1); 143 pushtree(x); 144 for(int i=fr[x];i;i=mo[i].pr) 145 { 146 int to=mo[i].to; 147 if(to!=fa[x]&&to!=son[x]) pushtree(to); 148 } 149 ans[x]=find(1,k[x]); 150 if(son[x]) 151 { 152 pushvector(son[x],x); 153 swap(v[son[x]],v[x]); 154 } 155 for(int i=fr[x];i;i=mo[i].pr) 156 { 157 int to=mo[i].to; 158 if(to!=fa[x]&&to!=son[x])pushvector(x,to); 159 } 160 if(!op) 161 { 162 clear(1); 163 for(int i=0;i<v[x].size();i++) check[v[x][i].c]=0; 164 } 165 } 166 signed main() 167 { 168 n=rd(); 169 for(int i=1,x,y;i<n;i++) 170 { 171 x=rd(),y=rd(); 172 add(x,y),add(y,x); 173 } 174 for(int i=1;i<=n;i++)k[i]=rd(); 175 dfs1(1); 176 //for(int i=1;i<=n;i++) cout<<son[i]<<endl; 177 m=rd(); 178 build(1,1,m); 179 for(int i=1,x,y;i<=m;i++) 180 { 181 x=rd(),y=rd(); 182 if(!p[y]) p[y]=++cnt; 183 v[x].push_back((flag){i,p[y]}); 184 } 185 dfs2(1,0); 186 int q=rd(); 187 for(int i=1,x,y;i<=q;i++) 188 { 189 x=rd(); 190 printf("%lld\n",ans[x]); 191 } 192 } 193 /* 194 g++ -g 1.cpp -o 1 195 ./1 196 5 197 1 2 198 2 3 199 3 4 200 2 5 201 2 1 1 1 1 202 2 203 2 1 204 4 2 205 3 206 1 207 3 208 5 209 */ 210 /* 211 g++ 1.cpp -o 1 212 ./1 213 10 214 3 10 215 2 5 216 3 2 217 2 6 218 1 9 219 8 7 220 7 4 221 3 8 222 3 1 223 15 47 23 22 9 16 45 39 21 13 224 10 225 10 7 226 9 3 227 5 1 228 5 2 229 9 4 230 10 9 231 2 4 232 10 1 233 2 6 234 7 9 235 3 236 1 237 2 238 3 239 */
T3就很好办了,这就是一个简单的假期望,出题人好像出漏了,这个题正解复杂度$O(n^{2}m)$,但考完一看被DeepinC的mlog算法按在地上摩擦,手动打一下柿子应该是$ans=\frac{\sum\limits_{i=1}^{m}(i^k-(i-1)^k)*w[i]}{m^k}*(n-k+1)$,在下一看数据范围被误导了一下,然后写了个DP方程,表示连做k道题,最大值为j的方案数,柿子大概长这样:$f[i][j]=f[i-1][j]*(j-1)+\sum \limits_{k=1}^{j}f[i-1][k]$,然后这是$O(n^{3})$的,稍加优化干成$O(n^{2})$,也能把正解吊着打。。想去找一下出题人嘲讽一下。。而且出题人数据自己打自己脸是什么鬼。。为什么数据范围k<=n然后有一个k>n的点,导致全场几乎都少了5分。。。。
#include<cstdio> #define p 1000000007 #define L long long L n,m,k,l,t,w,d; L pw(L b,L t,L a=1){for(;t;t/=2,b=b*b%p)if(t&1)a=a*b%p;return a;} int main(){ scanf("%lld%lld%lld",&n,&m,&k);if(k>n){puts("0");return 0;} for(int i=1;i<=m;++i)scanf("%lld",&w),t=pw(i,k),(d+=(t-l+p)%p*w)%=p,l=t; printf("%lld\n",d*(n-k+1)%p*pw(pw(m,k),p-2)%p); }
#include<iostream> #include<cstdio> #include<algorithm> #define int long long using namespace std; const int mod=1e9+7; int f[600][600],dp[600][600],w[600]; int rd() { int s=0,w=1; char cc=getchar(); while(cc<'0'||cc>'9') {if(cc=='-') w=-1;cc=getchar();} while(cc>='0'&&cc<='9') s=(s<<3)+(s<<1)+cc-'0',cc=getchar(); return s*w; } int qpow(int a,int k) { int ans=1; for(;k;k>>=1,a=a*a%mod) if(k&1) ans=ans*a%mod; return ans; } signed main() { int n=rd(),m=rd(),k=rd(); if(k>n) { puts("0"); return 0; } for(int i=1;i<=m;i++) w[i]=rd(); for(int i=1;i<=m;i++) f[1][i]=1; for(int t=2;t<=k;t++) { for(int i=1;i<=m;i++) { for(int j=1;j<i;j++) f[t][i]=(f[t][i]+f[t-1][j])%mod; f[t][i]=(f[t][i]+f[t-1][i]*i)%mod; } } int inv=qpow(m,k); inv=qpow(inv,mod-2); int ans=0; for(int i=1;i<=m;i++) { ans=(ans+f[k][i]*w[i])%mod; } ans=ans*inv%mod; ans=ans*(n-k+1)%mod; printf("%lld\n",ans); } /* g++ 3.cpp -o 3 ./3 5 4 3 2 1 3 5 */