https://ac.nowcoder.com/acm/contest/3007#question
前两天打的最后一场,过的题最少的一场......正好碰了盲区加状态差,也问题不大。
md,刚刚在门口吃东西撞了玻璃门,镜框坏了,被迫出门上街换了镜框。。。。。我真傻,真的。撞玻璃门是真实存在的。
然后补题记录思维。
A.配对 好像是个啥不等式,以前上课老师讲过,感觉差不多。排序后小的和大的组合一下即可。
G.括号序列 跟前一天的差不多,模拟配对即可。
J.签到题 冷静推式子...话说我也推了半个小时???真的一点也不冷静其实我。
---------------然后就陷入自闭-----------
F.十字阵列 这个F第一次看样例根本看不懂,后来才知道要求的是wij*(i+j) 。加个乘号真的更清晰好吗。不然 光一个 Σwij(i+j) 感觉真的8太行
反正我有问题,看了好久加好几次通知才看出来。看懂了就算一下贡献就过了。
D.重排列
开始只知道求A的重排列,那对A排序肯定不影响,没发现A的任意排列,也可以将B对应A的排列给找到,所以对B排序也不会影响。
然后对A对B都排个序,然后找每位 从A中能填多少种数,就是相应的B里二分查找一下有多少个A中的数小于等于B当前这个数,用upper_bound找一下,同时减去已经被前位占的数i,即可。
B.图
完全没听说过的基环树,基环内向树,处理环的经验相对欠缺,做不来。比赛的时候并未深入思考。看了代码发现也还行叭。就遍历过程中dfs时候优先处理环的答案,先把环的答案全处理出来,由于内向树,出度为1,用while就可以了。先把内部搞定,然后外部就是逐层加1了。然后标程处理环的时候模拟了栈,感觉还挺麻烦的。
参考别人的这份代码还不错,就理解下dfs到环,区分已经vis,ans到的答案怎么更新即可,遇到环就已经把这个环的答案全部记录上。
1 #include2 #ifndef ONLINE_JUDGE 3 #define debug(x) cout << #x << ": " << x << endl 4 #else 5 #define debug(x) 6 #endif 7 using namespace std; 8 typedef long long ll; 9 const int maxn=1e6+7; 10 const int inf=0x3f3f3f3f; 11 const int mod=1e9+7; 12 13 int vis[maxn]; 14 int to[maxn]; 15 int ans[maxn]; 16 int res; 17 18 void dfs(int x) 19 { 20 vis[x]=1; 21 if(vis[to[x]]) 22 { 23 if(!ans[to[x]]) 24 { 25 int now=x,num=1; 26 while(to[now]!=x) 27 { 28 now=to[now]; 29 num++; 30 } 31 now=x; 32 ans[x]=num; 33 while(to[now]!=x) 34 { 35 now=to[now]; 36 ans[now]=num; 37 } 38 res=max(res,num); 39 } 40 else 41 ans[x]=ans[to[x]]+1; 42 } 43 else 44 { 45 dfs(to[x]); 46 if(!ans[x]) ans[x]=ans[to[x]]+1; 47 } 48 res=max(res,ans[x]); 49 } 50 51 int main() 52 { 53 ios::sync_with_stdio(false); 54 cin.tie(0); 55 int n; 56 cin>>n; 57 for(int i=1,x;i<=n;++i) 58 { 59 cin>>x; 60 to[i]=x; 61 } 62 for(int i=1;i<=n;++i) 63 { 64 if(!vis[i]) dfs(i); 65 } 66 cout< endl; 67 return 0; 68 }
基环树优先考虑处理环。
C.汉诺塔
Dilworth定理比赛里我是真的想到了啥啥定理来着,前几天CF好像碰到过,想到了就是求最小升序划分就最长降序子序列(反正两个形容词都反一下就对了)。
然后我不会nlog的子序列求法,LIS没学好,其实是大概有印象的,维护一下新数组,二分去替换更新最优的答案。这题我是把x按从大到小排序的,然后就是求y同样降序的最小划分,也就是求y的最长上升子序列了。这样排序处理可以自己设计一下,更方便。
然后就是LIS的事情了,每次二分找,如果在新数组中找不到比当前这个y大的,(比当前大说明可以替换,就是扩充这一组,把大的换成更小的,保证了x更小,y更小),找不到,就新增一组,同时组号就是在新数组中的下标。
由于这里保证没有相同的x和y,所以upper_bound和lower一样可以。
#include#ifndef ONLINE_JUDGE #define debug(x) cout << #x << ": " << x << endl #else #define debug(x) #endif using namespace std; typedef long long ll; const int maxn=2e5+7; const int inf=0x3f3f3f3f; const int mod=1e9+7; struct node { int x,y,id; }a[maxn]; bool cmp(node a,node b) { return a.x>b.x; } int n,cnt,st[maxn],ans[maxn]; int main() { ios::sync_with_stdio(false); cin.tie(0); int n; cin>>n; for(int i=1;i<=n;++i) { cin>>a[i].x>>a[i].y; a[i].id=i; } sort(a+1,a+1+n,cmp); for(int i=1;i<=n;++i) { int pos=upper_bound(st+1,st+1+cnt,a[i].y)-st; st[pos]=a[i].y; if(pos>cnt) cnt=pos; ans[a[i].id]=pos; } cout< endl; for(int i=1;i<=n;++i) cout< ' '; cout<<endl; return
E.立方数
这题也挺搞,看了题面,又看到群里有人说Pollard_rho 然后我刚好接触过,(仅仅接触过,没完全懂)然后疯狂找板子改,然后疯狂T,其实这个大数质因子分解的复杂度是O(N^1/4)据说,然后T*(N^1/4)高达3e8过不去也正常,况且分解完map统计可能又会变大?
题解给的做法又是要脑子的。因为要找最大的a,使得a^3*b=n嘛。a最大也就1e6啊,显然可以筛1e6内素数,然后就有T*(n^1/3)/(ln(n^1/3)) 后面那个为素数密度。。。反正还是很大。
然后脑子转下?就有了筛n^1/4内素数,n^1/4内素数全部筛掉并统计三次方进答案,最后剩下的数要么是n^1/4以上数的三次方,要么对答案无贡献为啥呢?
假设如果剩下的数是(n^1/4以上的质数)^3 *b(b>1),那么b要么应该在前面被筛掉,要么应该大于n^1/4,乘起来反而大于n,显然矛盾了。
然后就证明完了,那么完了,脑子该去哪找?
所以记录下思维吧,其实先常规思考,最后尝试往更小范围的质数想,降低复杂度。
最后也可以二分,只是check嘛,方法很多。
1 #include2 #ifndef ONLINE_JUDGE 3 #define debug(x) cout << #x << ": " << x << endl 4 #else 5 #define debug(x) 6 #endif 7 using namespace std; 8 typedef long long ll; 9 const int maxn=31700; 10 const int inf=0x3f3f3f3f; 11 const int mod=1e9+7; 12 13 int vis[maxn]; 14 vector<int>pri; 15 vector ptri; 16 int main() 17 { 18 for(int i=2;i i) 19 { 20 if(!vis[i]) 21 { 22 pri.push_back(i); 23 ptri.push_back(1ll*i*i*i); 24 for(int j=i*i;j i) 25 vis[j]=1; 26 } 27 } 28 int t; 29 scanf("%d",&t); 30 while(t--) 31 { 32 ll n; 33 scanf("%lld",&n); 34 int ans=1; 35 for(int i=0;i i) 36 { 37 if(n%pri[i]==0) 38 { 39 while(n%ptri[i]==0) 40 { 41 ans*=pri[i]; 42 n/=ptri[i]; 43 } 44 while(n%pri[i]==0) 45 n/=pri[i]; 46 } 47 } 48 int t=pow(n,1.0/3); 49 for(int i=max(1,t);i<=t+1;++i) 50 if(1ll*i*i*i==n) {ans*=i;break;} 51 printf("%d\n",ans); 52 } 53 return 0; 54 }
I.导航系统
n=500,可为啥好多n^3代码都能过呢,1s能跑1e8多吗?我迷惑啦。n^3确实好理解。
就是对每个点,肯定至少有一条和其他点直接相连的边,然后考虑最小生成树的过程,这个直接相连的边肯定是这两个点之间的最短距离没毛病,通过最小生成树找到这n-1条边。
然后通过n-1条边还原出原图的最短距离,再和原图比较,这是我看了别人代码和题解的理解。然后我也n^3 floyd了,毕竟好理解,也能过。。。
1 #include2 #ifndef ONLINE_JUDGE 3 #define debug(x) cout << #x << ": " << x << endl 4 #else 5 #define debug(x) 6 #endif 7 using namespace std; 8 typedef long long ll; 9 const int maxn=3e5+7; 10 const int inf=0x3f3f3f3f; 11 const int mod=1e9+7; 12 13 ll mp[505][505]; 14 ll d[505][505]; 15 int ans[505],fa[505]; 16 struct edge 17 { 18 int u,v,w; 19 }e[maxn]; 20 int tot; 21 void addedge(int u,int v,int w) 22 { 23 e[tot].u=u; 24 e[tot].v=v; 25 e[tot++].w=w; 26 } 27 bool cmp(edge a,edge b) {return a.w<b.w;} 28 29 int find(int x) 30 { 31 if(fa[x]==-1) return x; 32 return fa[x]=find(fa[x]); 33 } 34 35 int main() 36 { 37 ios::sync_with_stdio(false); 38 cin.tie(0); 39 int n; 40 cin>>n; 41 for(int i=1;i<=n;++i) 42 { 43 for(int j=1;j<=n;++j) 44 { 45 cin>>mp[i][j]; 46 if(i!=j) addedge(i,j,mp[i][j]); 47 } 48 } 49 sort(e,e+tot,cmp); 50 memset(fa,-1,sizeof(fa)); 51 for(int i=1;i<=n;++i) 52 for(int j=1;j<=n;++j) 53 if(i==j) d[i][j]=0; 54 else d[i][j]=1e15; 55 int cnt=0; 56 for(int i=0;i i) 57 { 58 int u=e[i].u,v=e[i].v,w=e[i].w; 59 int t1=find(u),t2=find(v); 60 if(t1!=t2) 61 { 62 fa[t1]=t2; 63 ans[cnt++]=w; 64 d[u][v]=d[v][u]=w; 65 } 66 } 67 for(int k=1;k<=n;++k) 68 for(int i=1;i<=n;++i) 69 for(int j=1;j<=n;++j) 70 d[i][j]=min(d[i][j],d[i][k]+d[k][j]); 71 for(int i=1;i<=n;++i) 72 for(int j=1;j<=n;++j) 73 if(d[i][j]!=mp[i][j]) 74 {cout<<"No\n";return 0;} 75 cout<<"Yes\n"; 76 for(int i=0;i '\n'; 77 return 0; 78 }
H.云
神奇的扫描线。观察发现(发现不了) 处理相对运动好吧。。。又是物理。。。题解讲的还行,看代码的时候主要不知道投影坐标怎么算的。
然后发现列个式子就出来了,一个y=x+q-p,与y=-x联立,就可以解得,然后等比例放大并且横一下就可以,直接简化为 纵-横。
同时每个矩形投影为2个点,有左有右,左是标记开始统计这个,右是结束这个的统计。方便设为1,和-1,两个象限分别种类为0和1.
然后排序扫描线,对于每个是进入的矩形左端点,加上 在他之前进入并且没有出去的另一种 就是答案,保证没有重复,因为 我统计的是之前进入的另一个种类 对当前的影响,非常的神奇,扫描一遍就出来了。然后有个细节就是位置相同的,左端点先进入,种类倒无所谓,排序的时候注意处理一下即可。
1 #include2 #ifndef ONLINE_JUDGE 3 #define debug(x) cout << #x << ": " << x << endl 4 #else 5 #define debug(x) 6 #endif 7 using namespace std; 8 typedef long long ll; 9 const int maxn=4e5+7; 10 const int inf=0x3f3f3f3f; 11 const int mod=1e9+7; 12 13 struct node 14 { 15 int pos,lr,type; 16 }e[maxn]; 17 18 bool cmp(node a,node b) 19 { 20 return a.pos b.lr; 21 } 22 ll cnt[2]; 23 24 int main() 25 { 26 int n,m,x,y,p,q,k=0; 27 scanf("%d%d",&n,&m); 28 for(int i=0;i i) 29 { 30 scanf("%d%d%d%d",&x,&y,&p,&q); 31 e[k++]={q-p,1,0}; 32 e[k++]={y-x,-1,0}; 33 } 34 for(int i=0;i i) 35 { 36 scanf("%d%d%d%d",&x,&y,&p,&q); 37 e[k++]={q-p,1,1}; 38 e[k++]={y-x,-1,1}; 39 } 40 sort(e,e+k,cmp); 41 ll ans=0; 42 for(int i=0;i i) 43 { 44 cnt[e[i].type]+=e[i].lr; 45 if(e[i].lr==1) 46 ans+=cnt[e[i].type^1]; 47 } 48 printf("%lld\n",ans); 49 return 0; 50 }
然后这6套题差不多补完了,补了58道吧?除了5的大模拟和4的没懂的计数。至少要么比赛里出了,要么赛后看题解看好多份别人代码 加 自己写一遍甚至二种写法练下了。
补的还算细吧,希望能记住啦啦啦啦啦 。逃~
哪里不足或者迷惑,评论区可以交流。等一手互动。