2019 The Preliminary Contest for ICPC Asia Nanjing

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 }
View Code

这大概算是欧拉定理的活用吧。

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 }
View Code

然而开始写的复杂度却惊人的大,后来通过随机大数据调试发现问题出在了,这一句上:

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 }
View Code
原文地址:https://www.cnblogs.com/Kiritsugu/p/11447577.html