ICPC 2019-2020 North-Western Russia Regional Contest 补题部分

it2025-07-22  12

已做A、M,E和H思路已经有了没调AC 已补BEJH 最终已完成ABEJHM

B - Bad Treap

大佬题解 感觉这题就很玄学。。。

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0) #pragma GCC optimize(2) #include<set> #include<map> #include<cmath> #include<queue> #include<string> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef pair<double,int> pdi; typedef pair<int,int> pii; typedef long long ll; const int N=500010; vector<pdi> v; vector<int> ans; int main() { IO; int T=1; //cin>>T; while(T--) { int n; cin>>n; for(int i=1;i<=50000;i++) if(sin(i)>0) v.push_back({sin(i),i}); sort(v.begin(),v.end()); for(int i=-25000;i<=25000;i++) ans.push_back(i*v[0].second); for(int i=0;i<n;i++) cout<<ans[i]<<'\n'; } return 0; }

E - Equidistant

队友想的方法:先以1为根节点跑一边dfs,记录每个节点的深度,然后把m个关键点中,深度最大的点mx和深度最小的点mn拿出来,找到这两个点路径上的中点s( d e p m x − d e p s = d e p s − d e p l c a + d e p m n − d e p l c a dep_{mx}-deps=dep_s-dep_{lca}+dep_{mn}-dep_{lca} depmxdeps=depsdeplca+depmndeplca),找到该点后,以该点为根节点重建树,那么m个关键的的深度一定相等才满足题意。

感觉上述方法没有错但是一直wa14,最后我把以1为根节点跑dfs改成以任意一个关键点先跑一边dfs,那么深度最小的点就是根节点,上述式子都不变就AC了,很奇怪

最终队友举了个反例找到了以1为根跑dfs为什么会出问题。我hack我自己hhh

8 3 1 2 2 3 3 7 3 4 4 5 4 6 7 8 5 6 8 #define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0) #pragma GCC optimize(2) #include<set> #include<map> #include<cmath> #include<queue> #include<random> #include<bitset> #include<string> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<unordered_map> #include<unordered_set> using namespace std; typedef long long ll; typedef pair<int,int> pii; const int N=200010; int h[N],e[2*N],ne[2*N],idx; int n,m,c[N]; bool st[N]; int fa[N],dep[N]; void add(int a,int b) { e[idx]=b; ne[idx]=h[a]; h[a]=idx++; } void dfs(int u,int p) { fa[u]=p; dep[u]=dep[p]+1; for(int i=h[u];i!=-1;i=ne[i]) { int j=e[i]; if(j==p) continue; dfs(j,u); } } int lca(int a,int b) { while(a) { st[a]=1; a=fa[a]; } while(b) { if(st[b]) return b; b=fa[b]; } } int main() { IO; int T=1; //cin>>T; while(T--) { memset(h,-1,sizeof h); cin>>n>>m; for(int i=1;i<n;i++) { int a,b; cin>>a>>b; add(a,b),add(b,a); } for(int i=1;i<=m;i++) cin>>c[i]; dfs(c[1],0);//以任意一个关键跑dfs //dfs(1,0); 以1为根节点跑dfs int mx=-1,mn=-1; for(int i=1;i<=m;i++) { if(mx==-1||dep[mx]<dep[c[i]]) mx=c[i];//最大深度点的编号 if(mn==-1||dep[mn]>dep[c[i]]) mn=c[i];//最小深度点的编号 } int p=lca(mn,mx);// 向上标记求lca if((dep[mx]+2*dep[p]-dep[mn])%2) { cout<<"NO\n"; continue; } int deps=(dep[mx]+2*dep[p]-dep[mn])/2; int k=dep[mx]-deps; int s=mx; while(k--) s=fa[s]; dfs(s,0); bool ok=1; for(int i=2;i<=m;i++) if(dep[c[i]]!=dep[c[i-1]]) { ok=0; break; } if(ok) { cout<<"YES\n"; cout<<s<<'\n'; } else cout<<"NO\n"; } return 0; }

J - Just the Last Digit

对于任意两个点 i i i j j j来说,两个点是否存在一条边最终只可能有1条路径的差别,因此只需要知道路径个数的最后一位即可区分是否该两点直接有直接路径相连。

对于两个点之间的路径个数我们可以根据递推求出 考虑 i → j i\to j ij直接的路径条数 我们可以考虑枚举中间点 k k k i → k → ⋯ → j i\to k\to \dots \to j ikj(注意 i → k i\to k ik这条路径是表示直接路径不是间接路径)不难得出 i → j i\to j ij路径个数 c n t i → j = ∑ k = i + 1 j − 1 c n t k → j ( i → k ) cnt_{i\to j}=\sum_{k=i+1}^{j-1}{cnt_{k\to j}}(i\to k) cntij=k=i+1j1cntkj(ik) 如果当前所求 c n t i → j cnt_{i\to j} cntij与题中所给路径个数有出处那么说明 i → j i\to j ij存在直接路径,否则不存在。

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0) #pragma GCC optimize(2) #include<set> #include<map> #include<cmath> #include<queue> #include<random> #include<bitset> #include<string> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<unordered_map> #include<unordered_set> using namespace std; typedef long long ll; typedef pair<int,int> pii; const int N=510; char g[N][N]; int d[N][N]; int n; int main() { IO; int T=1; //cin>>T; while(T--) { cin>>n; for(int i=1;i<=n;i++) cin>>g[i]+1; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) { int cnt=0; for(int k=i+1;k<j;k++) if(d[i][k]) cnt=(cnt+g[k][j]-'0')%10; if(cnt!=g[i][j]-'0') d[i][j]=1; } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) cout<<d[i][j]; cout<<'\n'; } } return 0; }

我看到这个题给的条件时根本无从下述,我心里想给我路径个数可能都不会求,题目中只给了路径个数%10的结果。

当时心里一直在想一个公式 c n t i → j = c n t i → k × c n t k → j cnt_{i\to j}=cnt_{i\to k}×cnt_{k\to j} cntij=cntik×cntkj,这里 k k k i i i j j j之间任意一个点即可,而没想的上述计数的方法。

H - High Load Database

队友想的,二分+二分两个log,由于记忆化不会被数据卡掉。

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0) #pragma GCC optimize(2) #include<set> #include<map> #include<cmath> #include<queue> #include<random> #include<bitset> #include<string> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<unordered_map> #include<unordered_set> using namespace std; typedef long long ll; typedef pair<int,int> pii; const int N=1000010; ll a[N],s[N]; int ans[N]; int n; ll mx; void solve(int t) { ans[t]=0; if(t<mx) return; for(int i=1;i<=n;i++) { int l=i,r=n; while(l<r) { int mid=l+r+1>>1; if(s[mid]-s[i-1]<=t) l=mid; else r=mid-1; } i=r; ans[t]++; } } int main() { IO; int T=1; //cin>>T; while(T--) { cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; mx=max(mx,a[i]); s[i]=s[i-1]+a[i]; } int q; cin>>q; memset(ans,-1,sizeof ans); while(q--) { int t; cin>>t; if(ans[t]!=-1) { if(ans[t]) cout<<ans[t]<<'\n'; else cout<<"Impossible"<<'\n'; } else { solve(t); if(ans[t]) cout<<ans[t]<<'\n'; else cout<<"Impossible"<<'\n'; } } } return 0; }
最新回复(0)