第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛题解
题目地址 https://www.nowcoder.com/acm/contest/90#question
A题:维护前n-1的和sum 第n个是sum+1
B题:用哈希维护该点到根的字符串 然后一共维护26个,因为26秒和0秒是一样的,然后询问就是二分最长公共前缀然后比较第一个不同的字符的字典序数
#include <iostream> #include <cstring> #include <stdio.h> #include <vector> #include <algorithm> using namespace std; const int maxn = 1e5+9; unsigned long long int val[26][maxn]; int fa[20][maxn]; int step[maxn]; int str[maxn]; int tot; int head[maxn]; struct edge{ int v,nex; }e[maxn]; void addedge(int u,int v){ e[tot] = (edge){v,head[u]}; head[u] = tot++; } void init(int n){ for(int i=1;i<20;i++){ for(int j=1;j<=n;j++){ fa[i][j] = fa[i-1][fa[i-1][j]]; } } } int getfa(int now,int k){ for(int i=0;i<20;i++){ if((k&(1<<i))!=0){ now = fa[i][now]; } } return now; } int deep[maxn]; void dfs(int now,int time){ for(int i=head[now];i!=-1;i=e[i].nex){ int v = e[i].v; val[time][v] = val[time][now]*26+(str[v]+time*step[v])%26; deep[v] = deep[now]+1; dfs(v, time); } } unsigned long long int quick(unsigned long long int a,unsigned long long int b){ unsigned long long int ret = 1; while (b) { if(b&1){ ret*=a; } a*=a; b>>=1; } return ret; } int getans(int u,int v,int time){ int l = 0; int r = min(deep[u],deep[v]); int ret = 0; while (l<=r) { int mid = l+r>>1; int fau = getfa(u, mid); int fav = getfa(v, mid); unsigned long long int tmp1,tmp2; tmp1 = val[time][u]-val[time][fau]*quick(26,mid); tmp2 = val[time][v]-val[time][fav]*quick(26,mid); if(tmp1==tmp2){ ret = mid; l=mid+1; }else{ r = mid-1; } } if(ret==deep[u] && deep[u]==deep[v]) return 0; if(ret==deep[u]){ return -1; } if(ret==deep[v]){ return 1; } int fau = getfa(u, ret); int fav = getfa(v, ret); unsigned long long int tmp1,tmp2; tmp1 = val[time][fau]-val[time][fa[0][fau]]*26; tmp2 = val[time][fav]-val[time][fa[0][fav]]*26; if(tmp1<tmp2){ return -1; }else{ return 1; } } int main() { int T; scanf("%d",&T); while (T--) { tot = 0; memset(head, -1, sizeof(head)); int n; scanf("%d",&n); fa[0][1] = 1; char tmp[10]; for(int i=2;i<=n;i++){ scanf("%d",&fa[0][i]); addedge(fa[0][i],i); scanf("%s",tmp); str[i] = tmp[0]-'a'; scanf("%d",&step[i]); } init(n); deep[1] = 0; for(int i=0;i<26;i++){ dfs(1,i); } int qq ; scanf("%d",&qq); while (qq--) { int u,v,t; scanf("%d%d%d",&u,&v,&t); int ans = getans(u,v,t%26); if(ans<0){ puts("<"); }else if(ans==0) { puts("="); }else{ puts(">"); } } } }
C题:按k先分组 然后就是糖果传递这题 注意特判n==k
#include <iostream> #include <cstring> #include <stdio.h> #include <vector> #include <algorithm> using namespace std; typedef long long int ll; const ll maxn = 1e6+9; vector<ll> v[maxn]; ll a[maxn]; ll vis[maxn]; ll pp[maxn]; ll ck(ll index){ ll sum = 0; ll bbb = v[index].size(); for(ll i=0;i<bbb;i++){ sum+=v[index][i]; } ll ave = sum/bbb; pp[1] = 0; for(ll i=2;i<=bbb;i++){ pp[i] = pp[i-1]+v[index][i-2]-ave; } sort(pp+1,pp+1+bbb); ll mid = pp[(bbb>>1)]; ll ret = 0; for(ll i=1;i<=bbb;i++){ ret+=abs(pp[i]-mid); } return ret; } int main() { ll n,k; scanf("%lld%lld",&n,&k); k++; ll all = 0; for(ll i=0;i<n;i++){ scanf("%lld",&a[i]); all+=a[i]; } if(k==n+1){ k=n; } ll p=0; for(ll i=0;i<n;i++){ if(vis[i]==0){ ll nex = i; while(vis[nex]==0){ vis[nex] = 1; v[p].push_back(a[nex]); nex = (nex+k)%n; } p++; } } ll fl = 0; for(ll i=0;i<p;i++){ ll tmp = 0; ll dd = v[i].size(); for(ll j=0;j<dd;j++){ tmp+=v[i][j]; } if(tmp*n!=all*dd){ fl = 1; break; } } if(all%n!=0 || fl ==1 ){ puts("gg"); }else{ ll ans = 0; for(ll i=0;i<p;i++) ans+=ck(i); printf("%lld\n",ans); } return 0; }
D题 把串反过来做最长公共子序列
E题 枚举转折点,然后统计相同的边的长度
#include <iostream> #include <cstring> #include <vector> #include <stdio.h> #include <map> using namespace std; typedef long long int ll; const int maxn = 1e3+9; map<ll,ll> mapp; int x[maxn],y[maxn]; int main() { int T; scanf("%d",&T); while(T--){ int n;scanf("%d",&n); ll ans = 0; for(int i=0;i<n;i++){ scanf("%d%d",&x[i],&y[i]); } for(int i=0;i<n;i++){ mapp.clear(); for(int j=0;j<n;j++){ if(i==j) continue; ll dis = (x[j]-x[i])*(x[j]-x[i])+(y[j]-y[i])*(y[j]-y[i]); if(mapp.find(dis)==mapp.end()){ mapp[dis] = 1; }else{ ll t = mapp[dis]; mapp[dis]++; ans+=t; } } } if(ans!=0) printf("%lld\n",ans*2); else puts("WA"); } }
F题 我的做法特别傻,先把问题转化为 假设n*n的质因子有k个,然后任意取这k个,只要乘积不超过n的就算一种情况,然后我就搞了个map维护 对于前x个 能构成的乘积小于n的情况,然后考虑第x个质因数,可以不取,可以只取一个,可以和之前的乘起来然后构成新的小于n的情况,因为n*n的质因子就是2倍的n的质因子,所以我跑2遍完事,事实上是2倍的n的质因子个数好像是
#include <iostream> #include <cstring> #include <vector> #include <stdio.h> #include <map> using namespace std; typedef long long int ll; const int maxn = 1e5+9; map<ll,ll> mapp; vector<int> v; ll p[maxn]; int main() { int T; scanf("%d",&T); while(T--){ ll n; scanf("%lld",&n); ll zz = n; v.clear(); mapp.clear(); for(int i=2;i<maxn;i++){ while(n%i==0){ v.push_back(i); n/=i; } } if(n!=1) v.push_back(n); mapp[1] = 1; int cnt =0; for(int i=0;i<v.size();i++){ int tt = cnt; for(int j=0;j<tt;j++){ ll tmp = (ll)p[j]*v[i]; if(tmp>zz) continue; if(mapp.find(tmp)==mapp.end()){ mapp[tmp] = 1; p[cnt++] = tmp; } } if(mapp.find(v[i])==mapp.end()){ mapp[v[i]] = 1; p[cnt++] = v[i]; } } for(int i=0;i<v.size();i++){ int tt = cnt; for(int j=0;j<tt;j++){ ll tmp = (ll)p[j]*v[i]; if(tmp>zz) continue; if(mapp.find(tmp)==mapp.end()){ mapp[tmp] = 1; p[cnt++] = tmp; } } if(mapp.find(v[i])==mapp.end()){ mapp[v[i]] = 1; p[cnt++] = v[i]; } } printf("%d\n",mapp.size()); } }
G题 模拟题
H题 qls教的,题意是求首先对l个求染色,然后对剩下没有用到的颜色拿出来m个作为集合,事实上是求(染色方案,颜色集合)这样的对有多少个,我们先考虑颜色集合,先c(n,m) 然后在对球染色 只要颜色不是这m个就行 所以答案是c(n,m)*(n-m)^l 这里注意预处理frac和inv是可以线性的
I题 水题
J题 每次对n-1个加一,等价于对一个减1,那么最少的步数肯定是把所有值都减到最小值,就是步数,然后最后的值就是最小值加步数
K题 模拟题
L题 对两个数质因数分解,k次幂就是质因数的个数乘k,然后判断质因数是否一样
include <iostream> #include <cstring> #include <stdio.h> using namespace std; typedef long long int ll; const int maxn = 4e4; ll c1[maxn]; ll c2[maxn]; int vis[maxn]; int prime[maxn]; int main() { int p = 0; for(int i=2;i<maxn;i++){ if(vis[i]==0){ prime[p++] = i; for(int j=2;i*j<maxn;j++){ vis[i*j] = 1; } } } int T; scanf("%d",&T); while(T--){ ll x,a,y,b; scanf("%lld%lld%lld%lld",&x,&a,&y,&b); memset(c1,0,sizeof(c1)); memset(c2,0,sizeof(c2)); for(int i=0;i<p;i++){ int tt = prime[i]; if(x%tt==0){ while(x%tt==0){ c1[i]++; x/=tt; } c1[i]*=a; } } for(int i=0;i<p;i++){ int tt = prime[i]; if(y%tt==0){ while(y%tt==0){ c2[i]++; y/=tt; } c2[i]*=b; } } int fl = 0; for(int i=0;i<p;i++){ if(c1[i]!=c2[i]){ fl = 1; break; } } if(x==y && fl==0){ puts("Yes"); }else{ puts("No"); } } return 0; }