1001:CCPC直播 字符串处理,几个if语句
1002:口算训练 前缀和处理<=根号n的因数,大于根号n的因数每个数至多有一个,用vector存下每个大因数的位置,map离散化。查询的时候lower_bound看是否存在即可。
1003:缺失的数据范围 式子单调增,二分答案。防溢出,需要double。
1004:寻宝游戏 神奇dp dp[i][j][x][y]表示走到(i,j)位置,有x个已经过的格子未统计,y个未经过的格子统计了的最大总分。往右和往下走的时候分别转移。往下走转移时,要从大到小选取这一行末及下一行首未经过的若干统计。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); return x*f; } const int N=55; int n,m,k,a[N][N],b[N][N][N],f[N][N][22][22],sum,ans,tmp; vector<int> vec[N]; void Max(int &x,int y){x=max(x,y);} bool cmp(int A,int B){return A>B;} int main() { int T=read(); while (T--) { n=read();m=read();k=read(); for (int i=1;i<=n;i++) vec[i].clear(); memset(b,0,sizeof(b)); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { a[i][j]=read();if (i==1) continue; for (int k=1;k<j;k++) b[i][j][++*b[i][j]]=a[i][k]; for (int k=j+1;k<=m;k++) b[i][j][++*b[i][j]]=a[i-1][k]; sort(b[i][j]+1,b[i][j]+*b[i][j]+1,cmp); } memset(f,-1,sizeof(f)); f[1][1][0][0]=0; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) for (int k1=0;k1<=k;k1++) for (int k2=0;k2<=k;k2++) if (tmp=f[i][j][k1][k2],tmp!=-1) { if (j!=m||i==n)//right { Max(f[i][j+1][k1+1][k2],tmp); Max(f[i][j+1][k1][k2],tmp+a[i][j]); } if (i!=n)//down { sum=0; for (int z=0;z<=k-k2;z++) sum+=z==0?0:b[i+1][j][z], Max(f[i+1][j][k1+1][k2+z],tmp+sum), Max(f[i+1][j][k1][k2+z],tmp+a[i][j]+sum); } } ans=0; for (int k1=0;k1<=k;k1++) Max(ans,f[n][m+1][k1][k1]); printf("%d\n",ans); } return 0; }
1005:奢侈的旅行 cost=log((level+a[i])/level),那么sigma_cost=log((1+a1)/1)+log((1+a1+a2)/(1+a1))+log((1+a1+a2+a3)/(1+a1+a2))+...,
根据log的变换,sigma_cost=log(1+a1+a2+...+ak)。要使得sigma_cost最小,即使得所有经过的ai之和最小。而每一条边cost>=bi,即level(也就是ai前缀和)<=ai/(2^bi-1),所以ai之和越小越好啦,跟上一个限制的目的一样。
按照ai跑个最短路,走每一条边的时候看限制是否通过即可。(现场大脑死机没想出log的转换,回家种地T_T)
1006:对称数 莫队+分块真的是可以过的啦,括号序列版多了两倍常数就光荣TLE了,以后树分块还是写直接分块、树上跳跃、然后去掉lca、查询时要特判的版本吧。(一开始想莫队+set,没有算好复杂度,结果后来没有时间改掉括号序列,一个是时间复杂度算得不够仔细,一个是转思路不够快)。莫队复杂度现场推,卡均值还是TTT。
正解似乎是某种套路。主席树+二分。出现不出现相当于异或。当权值区间[l,r]中实际的权值异或和=w[l]^w[l+1]^...^w[r](全取1次的异或和),那么大概率[l,r]中的权值都出现奇数次(w为随机权值,小概率被卡)。这样的话对于每一个点到根的链维护主席树(为什么要用主席树呢?因为主席树支持相减/相异或,比较方便统计出现次数),四棵主席树异或一下就能维护出l->r的路径。然后二分找没有出现过的最小权值。
bitset暴力异或+分块卡空间也是一种可行做法。
1007:赛题分析 序列取Min
1008:quality算法 图论转换题。由于B图中的每个点都只有两条边连出,不看B点, 就是一条连接A中两个点的边,由此A变成了一张完全图。问题转换成在A中选择n条边定向,并且每个点有且仅有一条入边,使得这n条边的权值最小。
基环森林是一个符合题意的构造。
由于有n^2条边,不能按照kruskal一样直接对边排序,我们从高位到低位按位对点权进行分治。
尽量在该位相同的块中连边,按照块的大小决策需要在块之间连多少边。
如果两个块的大小都>=3,那么内部必然能构成基环森林。中间不连边。
如果其中一个块的大小<3,连一条边成链。
两个块的大小都<3,连两条边构成环。
如果要连边就暴力枚举。时间复杂度O(nloga)。
//记得要判层数d==-1跳出,有可能点权都相等。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int read() 5 { 6 int x=0,f=1;char ch=getchar(); 7 while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} 8 while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 9 return x*f; 10 } 11 const int N=300005; 12 const int inf=0x3f3f3f3f; 13 int n,a[N],Mn,Mc,tmp; 14 ll ans; 15 void solve(int l,int r,int id) 16 { 17 if (l==r||id==-1) return; 18 if (l+1==r) {ans+=a[l]^a[r];return;} 19 int cnt=l-1; 20 for (int i=l;i<=r;i++) 21 if (!((a[i]>>id)&1)) cnt=i;else break; 22 if (cnt==r||cnt==l-1) {solve(l,r,id-1);return;} 23 else if (cnt-l+1>=3&&r-cnt>=3) {solve(l,cnt,id-1);solve(cnt+1,r,id-1);return;} 24 25 Mn=Mc=inf; 26 for (int i=l;i<=cnt;i++) 27 for (int j=cnt+1;j<=r;j++) 28 if (tmp=a[i]^a[j],tmp<Mn) Mc=Mn,Mn=tmp; 29 else if (tmp<Mc) Mc=tmp; 30 if (cnt-l+1<3&&r-cnt<3) ans+=(ll)Mn+Mc; 31 else ans+=Mn; 32 solve(l,cnt,id-1);solve(cnt+1,r,id-1); 33 } 34 int main() 35 { 36 int T=read(); 37 while (T--) 38 { 39 n=read();ans=0; 40 for (int i=1;i<=n;i++) a[i]=read(); 41 sort(a+1,a+n+1); 42 solve(1,n,30); 43 printf("%lld\n",ans); 44 } 45 return 0; 46 }
1009:SA-IS后缀数组 fy:快快快,后缀数组裸题!~10分钟后~忘记板子怎么背了。
这道题会卡nlogn,由于它是比较相邻两个后缀的大小关系,如果第一位不相同那么直接判掉。否则可以根据后一位两个后缀的大小关系判断。
1010:回文树 每个点的点权在[1,n]随机,那么答案的数量是O(n)的。考虑每个点、每条边为对称中心,暴力bfs枚举所有答案即可。hash判断,注意bfs的时候同子树中的答案减去。时间复杂度O(nlogn)。
1011:代码派对 一开始想数据结构、扫描线什么的。。。果不其然地跑偏。
差分前缀和统计覆盖某个点的矩形个数sum[i][j]。如果直接统计C(sum[i][j],3])的和,会算重。
考虑两个方向(折半去重),减去同时覆盖(i,j)和(i-1,j)的矩形三元组个数,减去同时覆盖(i,j)和(i,j-1)的矩形三元组个数,再加上同时覆盖(i,j),(i-1,j-1)的矩形三元组个数,就是答案。
统计同时覆盖两个格子的矩形数可以通过将所有矩形的右下角左缩进或上缩进,同样以前缀和统计。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1005; 5 int n,xa,xb,ya,yb,sum[4][N][N]; 6 ll c3(int n){return (ll)n*(n-1)*(n-2)/6;} 7 void add(int x,int Xa,int Ya,int Xb,int Yb) 8 { 9 if (Xa>Xb||Ya>Yb) return; 10 sum[x][Xa][Ya]++; 11 sum[x][Xa][Yb+1]--; 12 sum[x][Xb+1][Ya]--; 13 sum[x][Xb+1][Yb+1]++; 14 } 15 int main() 16 { 17 int T;scanf("%d",&T); 18 while (T--) 19 { 20 scanf("%d",&n); 21 for (int x=0;x<4;x++) memset(sum[x],0,sizeof(sum[x])); 22 for (int i=1;i<=n;i++) 23 { 24 scanf("%d%d%d%d",&xa,&ya,&xb,&yb); 25 add(0,xa,ya,xb,yb); 26 add(1,xa,ya,xb,yb-1); 27 add(2,xa,ya,xb-1,yb); 28 add(3,xa,ya,xb-1,yb-1); 29 } 30 for (int i=1;i<=1000;i++) 31 for (int j=1;j<=1000;j++) 32 for (int x=0;x<4;x++) 33 sum[x][i][j]+=sum[x][i-1][j]+sum[x][i][j-1]-sum[x][i-1][j-1]; 34 ll ans=0; 35 for (int i=1;i<=1000;i++) 36 for (int j=1;j<=1000;j++) 37 ans+=c3(sum[0][i][j])-c3(sum[1][i][j-1])-c3(sum[2][i-1][j])+c3(sum[3][i-1][j-1]); 38 printf("%lld\n",ans); 39 } 40 return 0; 41 }
那么,加油!