别找了原来没有写过叫《朦胧》的我check过了。(慌的一匹)
总算是比较早的改完了一套题。
但是考的是个啥啊。。。
前两道题都很卡常导致我想到了正解但是都放弃了。
2e8的复杂度怎么可能能过?它还就真的过了(重测后)
但是在考场上你算出2e8的复杂度时我是绝对不会打的。
也不知道为什么这次的数据范围和时限这么毒瘤。
T3想到树dp但是时间不够了(光卡前两题的常数了)然后就挂了
用了一个19分的盖了30分,掉了5个rank。
但是总体来说状态还是不好。。。
T1:Median
不知道什么叫做中位数的请回小学回炉再造我教的那踢足球的都知道什么是中位数
刚开始看到那个奇怪的生成序列还想找什么规律,但是如果是纯粹的素数的确没有规律。
那个序列的生成就是随机数据!
但是线筛178500000实在是可怕,毕竟stdTLE70。(fixed)
在观察数据范围,有一个w<=k,有什么用?
如果是随机数据的话,那么就意味着:均匀分布,基本每个数都出现过。
虽说S2序列是2w级别的,那也大概就是每2个数分布有一个。
不难发现每次序列移动时只是删了一个数加了一个数,那么在随机数据下中位数变化并不大。
开桶。存k个数里每种数出现了几次,然后用指针找中位数,指针的移动范围并不大。
在k为偶数时更麻烦一些,你要把中位数指针再往右动一动来找到后继,其实不难。
注意卡常!
1 #include<cstdio> 2 int S2[10000005],buc[20000005],p[10000005],S[10000005];bool np[200000005]; 3 main(){//freopen("t.in","r",stdin); 4 register int n,w,k,mp=0,mcnt=0,rp=0,rcnt=0,cnt=0;scanf("%d%d%d",&n,&k,&w); 5 for(register int i=2;;++i){ 6 if(!np[i]){p[++cnt]=i;if(cnt>=n)break;} 7 for(register int j=1;j<=cnt&&i*p[j]<179424673;++j){ 8 np[i*p[j]]=1; 9 if(i%p[j]==0)break; 10 } 11 } 12 for(register int i=1;i<=n;++i)S[i]=1ll*i*p[i]%w,S2[i]=S[i/10+1]+S[i]; 13 for(register int i=1;i<=k;++i)buc[S2[i]]++; 14 mcnt=buc[0]; 15 const register int K=k-1>>1,Kr=k>>1; 16 while(mcnt<=K)mcnt+=buc[++mp]; 17 register long long ans=0; 18 for(register int i=1;i<=n-k+1;++i){ 19 register int rp=mp,rcnt=mcnt; 20 while(rcnt<=Kr)++rp,rcnt+=buc[rp]; 21 ans+=mp+rp; 22 --buc[S2[i]];++buc[S2[i+k]]; 23 if(S2[i]<=mp)--mcnt; 24 if(S2[i+k]<=mp)++mcnt; 25 while(mcnt<=K)mcnt+=buc[++mp]; 26 while(mcnt-buc[mp]>K)mcnt-=buc[mp--]; 27 } 28 printf("%lld",ans>>1);puts(ans&1?".5":".0"); 29 }
思路积累:
- 2e8也是正解复杂度
- 指针的灵活运用。对复杂度的保证。
T2:Game
贪心很显然了,问题就是不断的找最大值。
因为k没有出到1e5那种级别,其实就可以表明并不是离线预处理或者带log什么的。
这道题的特殊之处在于如果加入了一个大数那么它马上就会被拿走。
所以开桶加指针,不断从下一个数和指针的数中取最优决策,桶空了就移动指针。
极度卡常。不离散化的话过不去(那么理论上可以造出离散化没有优化效果的数据导致所有离散化的人依然TLE。。。)
没办法评测机老了。
1 #include<cstdio> 2 #include<algorithm> 3 #include<map> 4 using namespace std; 5 map<int,int>M; 6 int read(){ 7 register int p=0;register char ch=getchar(); 8 while(ch>57||ch<48)ch=getchar(); 9 while(ch>47&&ch<58)p=(p<<3)+(p<<1)+ch-48,ch=getchar(); 10 return p; 11 } 12 int a[100005],buc[100005],b[100005],re[100005];const int r[2]={-1,1}; 13 int main(){ 14 register int n=read(),k=read(),cnt=0; 15 for(int i=1;i<=n;++i)a[i]=b[i]=read(); 16 sort(b+1,b+1+n); 17 for(int i=1;i<=n;++i)if(b[i]!=b[i-1])M[b[i]]=++cnt,re[cnt]=b[i]; 18 for(int i=1;i<=n;++i)b[i]=M[a[i]]; 19 while(k--){ 20 register int p=read(),pnt=0;register long long ans=0; 21 for(register int i=1;i<p;++i)buc[b[i]]++,pnt=max(b[i],pnt); 22 for(register int i=p;i<=n;++i) 23 if(b[i]>=pnt)ans+=re[b[i]]*r[i-p&1^1]; 24 else { 25 ans+=re[pnt]*r[i-p&1^1]; 26 buc[pnt]--;buc[b[i]]++; 27 while(!buc[pnt])pnt--; 28 } 29 for(register int i=n-p+2;i<=n;++i){ 30 ans+=re[pnt]*r[i&1];buc[pnt]--; 31 while(!buc[pnt]&&pnt)pnt--; 32 } 33 printf("%lld\n",ans); 34 } 35 }
思路积累:
- 2e8也是正解复杂度
- 指针的灵活运用。对复杂度的保证。
T3:Park/Chase
题很好。
看到题目能看出是树形dp,但是细节稍多。
需要发现的规律:放面包屑的贡献是你的所有邻居点的总初始鸽子数减去你到的上一个点的初始鸽子数。
经过分类讨论可以证明。
然后可以发现同一条路径正着走反着走是不一样的,得分类讨论起点在哪里。
既然是树p那么就考虑是否在字树内了,也就是这条链是向上走的还是向下走的。
维护出这两种链其实并不难,按照上面说的贡献做好了。
然后考虑怎么更新答案。扫到节点p之后枚举儿子t,用到p节点位置的向上链和从t节点开始的向下链合并一下就好了。
为什么不用p下链和t上链呢,因为按照上面的贡献规则如果选了p,那么应该把p的上一个点的鸽子减去。
但是在你做的下链统计过程中你是把父亲节点直接当作来源点的,然而在刚才那种情况下t才是来源点。
所以只考虑p的上链和t的下链。
这又引出了一个问题:你只用了已更新的儿子的上链和未更新的儿子的下链合并,但是儿子的顺序是一定的。
这样的话未更新的儿子的上链和已更新的儿子下链没有合并过导致决策不一定最优。因为上面说过路径反过来不一样。
所以把儿子的顺序倒过来再跑一遍就结束了。
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 void Max(long long &x,long long y){if(x<y)x=y;} 5 long long dp[100005][102],DP[100005][102],ans,cg[100005];//dp=up DP=down 6 int w[100005],fir[100005],l[200005],to[200005],cnt,n,k; 7 int FIR[200005],L[200005],TO[200005],CNT; 8 void link(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;cg[a]+=w[b];} 9 void Link(int a,int b){L[++CNT]=FIR[a];FIR[a]=CNT;TO[CNT]=b;} 10 void dfs(int p,int fa){ 11 for(int i=1;i<=k;++i)dp[p][i]=cg[p],DP[p][i]=cg[p]-w[fa]; 12 for(int e=fir[p];e;e=l[e])if(to[e]!=fa){int t=to[e]; 13 dfs(t,p); 14 for(int i=0;i<=k;++i)Max(ans,dp[p][i]+DP[t][k-i]); 15 for(int i=0;i<=k;++i)Max(dp[p][i],dp[t][i]),Max(dp[p][i+1],dp[t][i]+cg[p]-w[t]); 16 for(int i=0;i<=k;++i)Max(DP[p][i],DP[t][i]),Max(DP[p][i+1],DP[t][i]+cg[p]-w[fa]); 17 } 18 for(int i=1;i<=k;++i)Max(ans,dp[p][i]); 19 } 20 void DFS(int p,int fa){ 21 for(int i=1;i<=k;++i)dp[p][i]=cg[p],DP[p][i]=cg[p]-w[fa]; 22 for(int e=FIR[p];e;e=L[e])if(TO[e]!=fa){int t=TO[e]; 23 DFS(t,p); 24 for(int i=0;i<=k;++i)Max(ans,dp[p][i]+DP[t][k-i]); 25 for(int i=0;i<=k;++i)Max(dp[p][i],dp[t][i]),Max(dp[p][i+1],dp[t][i]+cg[p]-w[t]); 26 for(int i=0;i<=k;++i)Max(DP[p][i],DP[t][i]),Max(DP[p][i+1],DP[t][i]+cg[p]-w[fa]); 27 } 28 for(int i=1;i<=k;++i)Max(ans,dp[p][i]); 29 } 30 int main(){ 31 scanf("%d%d",&n,&k); 32 for(int i=1;i<=n;++i)scanf("%d",&w[i]); 33 for(int i=1,x,y;i<n;++i)scanf("%d%d",&x,&y),link(x,y),link(y,x); 34 for(int i=1;i<=n;++i)for(int j=fir[i];j;j=l[j])Link(i,to[j]); 35 dfs(1,0);DFS(1,0);printf("%lld\n",ans); 36 //for(int i=1;i<=4;++i)for(int j=0;j<=k;++j)printf("%d %d:%lld %lld \n",i,j,dp[i][j],DP[i][j]); 37 }