B 题目链接
题目公式貌似复杂但实际拆解后就是power tower问题,见此处 。 想到用欧拉降幂,但是降幂时幂数的处理有点麻烦。
有一种精巧取模方式及其证明过程,见某位大哥的证明过程 。
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 1e6; 5 int tot=0; 6 bool vis[maxn+6]; 7 int prime[maxn+6],totient[maxn+6]; 8 ll a,b,m; 9 ll mmod(ll x,ll m){ 10 return x>=m?(x%m+m):x; 11 } 12 13 ll qPow(ll a,ll b,ll m){ 14 ll ret = 1ll; 15 while(b){ 16 if(b&1) ret = mmod(ret*a,m); 17 a = mmod(a*a,m); 18 b >>= 1; 19 } 20 return ret; 21 } 22 23 void init(){ 24 totient[1] = 1; 25 vis[1] = true; 26 for(int i=2;i<=maxn;++i){ 27 if(!vis[i]){ 28 vis[i] = true; 29 prime[tot++] = i; 30 totient[i] = i-1; 31 } 32 for(int j=0;j<tot;++j){ 33 if(i*prime[j]>maxn) break; 34 vis[i*prime[j]] = true; 35 if(i%prime[j]){ 36 totient[i*prime[j]] = totient[i] * (prime[j] - 1); 37 }else{ 38 totient[i*prime[j]] = totient[i] * prime[j]; 39 break; 40 } 41 } 42 } 43 // printf("tot %d ",tot); 44 // for(int i=1;i<=100;++i) 45 // cerr<<totient[i]<<endl; 46 } 47 48 ll solve(ll L ,ll R, ll p ){ 49 if(L==R||p==1) return mmod(a,p); 50 ll ret = qPow(a,solve(L+1,R,totient[p]),p); 51 // printf("%lld %lld %lld %lld ",L,R,p,ret); 52 return ret; 53 } 54 55 int main(){ 56 int T; 57 scanf("%d",&T); 58 init(); 59 while(T--){ 60 scanf("%lld%lld%lld",&a,&b,&m); 61 if(b==0) 62 printf("%lld ",1ll%m); 63 else 64 printf("%lld ",solve(1ll,b,m)%m); 65 } 66 return 0; 67 }
这大概算是欧拉定理的活用吧。
F 题目链接
题目大意是给出 n 的 permutation , 对于每一个元素 在其左右各不超过 K个数的范围里 递归地转移到不超过当前元素的最大元素上,若无则结束寻找,现在需要根据该permutation得到每一个元素的寻找链的长度。(T<=20;n,K<=1e5)
每个元素第一个寻找的下一个位置是固定的,用尺取法+set二分 可以预处理出所有元素的第一个元素的下一个位置 <O(nlogn)>,然后记忆化搜索即可<O(n)> 。因为搜到一个点后其后的转移是固定的。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn = 1e5+5; 4 5 set< int > S; 6 int N,K; 7 int permut[maxn]; 8 int lefnext[maxn],rignext[maxn]; 9 //bool vis[maxn]; 10 int ans[maxn]; 11 12 int getnext(int val){ 13 val = -val; 14 // auto ptr = upper_bound(S.begin(),S.end(),val);//正是此处增加了复杂度 15 auto ptr = S.find(val); 16 ptr++; 17 if(ptr == S.end()) return 0; 18 int ret = (*ptr); 19 ret = -ret; 20 return ret; 21 } 22 23 int DFS(int x){ 24 // printf("in DFS %d ",x); 25 if(x<=0) 26 return 0; 27 if(ans[x]) 28 return ans[x]; 29 int LRnext = max(lefnext[x],rignext[x]); 30 if(LRnext == 0) 31 return ans[x] = 1; 32 else 33 return ans[x] = DFS(LRnext) + 1; 34 } 35 36 int main(){ 37 // __clock_t stt = clock(); 38 // __clock_t mdt; 39 int T; 40 scanf("%d",&T); 41 while(T--){ 42 S.clear(); 43 scanf("%d%d",&N,&K); 44 for (int i = 1; i <= N; ++i) { 45 scanf("%d",&permut[i]); 46 lefnext[i] = rignext[i] = 0; 47 // vis[i] = false; 48 ans[i] = 0; 49 } 50 51 int L,R; 52 L = 1,R=1; 53 S.insert(-permut[R]); 54 lefnext[permut[R]] = getnext(permut[R]); 55 while(L<=N&&R<=R){ 56 if(R-L+1 == K+1 || R==N){ 57 rignext[permut[L]] = getnext(permut[L]); 58 S.erase(-permut[L]); 59 L++; 60 } 61 else{ 62 R++; 63 S.insert(-permut[R]); 64 lefnext[permut[R]] = getnext(permut[R]); 65 } 66 } 67 68 // mdt = clock(); 69 70 for(int i=1;i<=N;++i){ 71 // printf("%d : %d,%d ",i,lefnext[i],rignext[i]); 72 ans[i] = DFS(i); 73 } 74 for(int i=1;i<=N;++i) 75 printf("%d%c",ans[i],i==N?' ':' '); 76 } 77 // __clock_t ent = clock(); 78 // printf("%.3lf ", static_cast<double>(mdt - stt)/1000.0 ); 79 // printf("%.3lf ", static_cast<double>(ent - stt)/1000.0 ); 80 return 0; 81 }
然而开始写的复杂度却惊人的大,后来通过随机大数据调试发现问题出在了,这一句上:
auto ptr = upper_bound(S.begin(),S.end(),val);//正是此处增加了复杂度
换成 "ptr = S.find(val) ; ptr ++ ;" 后就是正常预估的复杂度。 ps : 要是 S中没有 val 怎么办?也可以先insert后再erase()。
实际上最简单的写法是:
auto ptr = S.upper_bound(val);
如此也是对的。
std::upper_bound() 与 std::set::upper_bound() 完全不同啊。要是用第一个虽然答案无异,但相当于搜了许多不必要的位置,增加了时间开销。(set不是线性存储)
H 题目链接
签到题。
给一个保证无负环但可能有负权边的DAG,依次给出六个询问,每个询问给出 u ,v (保证 u -> v之前无边)两点点号, 要求在该图上建立一条 u -> v 的带权边,权值最小且必须保证加边后图仍无负环,对于每隔询问进行加边操作且回答加边权值。
以v为起点跑 Bellman-Ford ,然后 使加 u -> v后得到一个零权重环即可。v 到 u 是最短路,所以加新边 u -> v后 即使有其他环其权重也必然大于等于零。
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 struct Edge 5 { 6 int from,to; 7 ll dist; 8 }; 9 vector<Edge> edges; 10 vector<int> g[1005]; 11 bool inq[1005]; 12 ll d[1005]; 13 int pre[1005]; 14 int cnt[1005]; 15 int n,m; 16 int a,b; 17 void init() 18 { 19 for(int i=0;i<=n-1;i++) 20 g[i].clear(); 21 edges.clear(); 22 } 23 void addedge(int from,int to,ll dist) 24 { 25 edges.push_back((Edge){from,to,dist}); 26 int num=edges.size(); 27 g[from].push_back(num-1); 28 } 29 bool spfa(int s) 30 { 31 queue<int> q; 32 memset(inq,0,sizeof(inq)); 33 memset(cnt,0,sizeof(cnt)); 34 memset(d,0x3f,sizeof(d)); 35 d[s]=0; 36 inq[s]=1; 37 q.push(s); 38 while(!q.empty()) 39 { 40 int u=q.front(); 41 q.pop(); 42 inq[u]=0; 43 for(int i=0;i<g[u].size();i++) 44 { 45 Edge e=edges[g[u][i]]; 46 if(d[e.to]>d[u]+e.dist) 47 { 48 d[e.to]=d[u]+e.dist; 49 pre[e.to]=g[u][i]; 50 if(!inq[e.to]) 51 { 52 q.push(e.to); 53 inq[e.to]=1; 54 if(++cnt[e.to]>n) 55 return false; 56 } 57 } 58 } 59 } 60 return true; 61 } 62 63 int main() 64 { 65 int T; 66 scanf("%d",&T); 67 while (T--) { 68 scanf("%d%d",&n,&m); 69 init(); 70 for(int i=0;i<=m-1;i++) 71 { 72 int a,b,c; 73 scanf("%d%d%d",&a,&b,&c); 74 addedge(a,b,c); 75 } 76 for (int i = 0; i < 6; ++i) { 77 scanf("%d%d",&a,&b); 78 if(spfa(b)) 79 { 80 if(d[a] == d[1004]) d[a] = -1000000000;//无路径 81 ll wei = d[a]; 82 printf("%lld ",-1*d[a]); 83 addedge(a,b,-1*d[a]); 84 } 85 } 86 87 } 88 return 0; 89 }