The Preliminary Contest for ICPC Asia Nanjing 2019ICPC南京网络赛

 

B.super_log (欧拉降幂)

•题意

定一个一个运算log*,迭代表达式为

给定一个a,b计算直到迭代结果>=b时,最小的x,输出对m取余后的值 

•思路

$log*_{a}(1)=1+log*_{a}(0)=1-1=0$          

$log*_{a}(a)=1+log*_{a}(log_{a}(a))=1+log*_{a}(1)=1$    

$log*_{a}(a^{a})=1+log*_{a}(a)=2$                           

....

以此类推得$log*_{a}(a^{a^{a^{a^{a^{...}}}}})=b$    (共b个a) 

接下来接转化为$a^{a^{a^{a^{a^{...}}}}}$  (共b个a)  对m取模得结果

ps.如果对欧拉降幂不熟悉的话可以先看一下这个题(戳我~)

利用欧拉降幂

$a^{b}=\begin{cases}a^{b\%\varphi(p)}  \ \ \ \ \ \ \ \ \ \  gcd(a,p)=1 \\ a^{b} \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \   gcd(a,p)\neq 1,b \leqslant \varphi(p)\\a^{b\%\varphi(p)+\varphi(p)}  \ \ gcd(a,p)\neq1,b\geqslant \varphi(p)  \\ \end{cases}$

递归求解,至多走到2log层模数就会变成1,返回答案就行了。

但是在求$a^{a}\%\varphi(p)$时怎么计算$a$和$\varphi(p)$的大小呢

利用取模方法!

在取模时,如果大于$\varphi(p)$就取模 x=x%mod,否则不取模x=x,就可以当作a和p的互素处理

具体证明请看这里

 •代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 ll qpow(ll a,ll b,ll mod)//快速幂
 5 {
 6     ll res=1;
 7     while(b)
 8     {
 9         if(b&1)
10             res=res*a>mod?res*a%mod+mod:res*a;
11         a=a*a>mod?a*a%mod+mod:a*a;
12         b>>=1;
13     }
14     return res;
15 }
16 
17 ll phi(ll x)//求x的欧拉函数值
18 {
19     ll res=x;
20     for(int i=2;i*i<=x;i++)
21     {
22         if(x%i==0)
23         {
24             while(x%i==0)
25                 x/=i;
26             res=res-res/i;
27         }
28     }
29     if(x>1)
30         res=res-res/x;
31     return res;
32 }
33 
34 ll solve(ll a,ll b,ll m)
35 {
36     if(m == 1)
37         return 0;
38     if(b==0||a==1)
39         return 1ll;
40 
41     ll p=phi(m);
42     return qpow(a,solve(a,b-1,p),m);
43 }
44 
45 int main()
46 {
47     int t;
48     cin>>t;
49     while(t--)
50     {
51         ll a,b,m;
52         cin>>a>>b>>m;
53         cout<<solve(a,b,m)%m<<endl;
54     }
55 }
View Code

 

H. Holy Grail(最短路floyd)

 •题意

给一个加权有向图,然后让你给你六个顶点,添六条边,

但是添边是有限制的。每次添边的权值要最小,并且不能构成负环,

 

 •思路

可以先根据已知边跑floyd,获得此时两点u->v的最短路,然后v->u的权值等于u->v的最短路取反

然后再根据此时已知边继续跑floyd,再加边

进行6次floyd,加6条边

 dij不能处理负权pass!

 

(在比赛时一直在想加上u->v这条边也不能构成环的情况然后就不知道该怎么做了...

然而看出题人题解貌似没有这个意思....??题意描述不清喵喵喵?)

 •代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inf 0x3f3f3f3f3f3f3f3f
 5 int n,m;
 6 ll d[305][305];
 7 
 8 void Init()
 9 {
10     for(int i=1;i<=n;i++)
11         for(int j=1;j<=n;j++)
12             d[i][j]=inf;
13 }
14 
15 void floyd()
16 {
17     for(int k=1;k<=n;k++)
18         for(int i=1;i<=n;i++)
19             for(int j=1;j<=n;j++)
20                 d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
21 }
22 int main()
23 {
24 //    freopen("C:\\Users\\14685\\Desktop\\C++workspace\\in&out\\contest","r",stdin);
25     int t;
26     scanf("%d",&t);
27     while(t--)
28     {
29         scanf("%d%d",&n,&m);
30         Init();
31         int u,v;
32         ll k;
33         for(int i=1;i<=m;++i)
34         {
35             scanf("%d%d%lld",&u,&v,&k);
36             d[u+1][v+1]=k;
37         }
38         for(int i=1;i<=6;++i)
39         {
40             floyd();
41             scanf("%d%d",&u,&v);
42             printf("%lld\n",-1ll*d[v+1][u+1]);
43             d[u+1][v+1]=-1ll*d[v+1][u+1];
44         }
45     }
46 }
View Code

 

F.Greedy Sequence(思维+滑动窗口)

•题意

  给你一个包含 n 个数的序列 a,其中 a 中存的数为 1~n 的排列;

  定义一个二维数组 s;

  s 中一共有 n 行,每行有 n 个数;

  定义 s 数组:

    ① $s_{i}[1]=i$

    ②$\forall\  i \in[1,n],j \in [2,n],s_{i,j} \le s_{i,j-1}$,即 $s_i$是个非增序列;

    ③对于 $s_i$ 中的第 j( j > 1 ) 个数,$s_{i,j}$ 在 a 中的位置与 $s_{i,j-1}$ 在 a 中的位置之差的绝对值不能超过 k;

    ④ $s_i$ 中的第 j( j > 1) 个数,要尽可能的大;

    ⑤如果 $s_{i,j}$ 后,在 a 中找不到满足条件 ②③ 的数,并且 $s_i$ 不足 n 个数,用 0 填充剩余的数;

  输出 |s1|,|s2|,...,|sn|,|si|表示 s 中第 i 行包含的数的个数;

•思路

在set为空时插入0,是为了upper_bound()容易找,

利用upper_bound()需要-1,如果upper_bound()=begin()的话需要判断

插入0后就不需要判断了,方便操作

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e5+5;
 4 int a[maxn];
 5 int R[maxn],L[maxn];
 6 int s[maxn];
 7 set<int> f;
 8 int n,k;
 9 
10 void Solve()
11 {
12     f.clear();
13     f.insert(0);
14     int index=2;///index代表下标
15     for(;index<=min(k+1,n);index++)
16         f.insert(a[index]);
17 
18     for(int i=1;i<=n;i++)
19     {
20         auto it=f.upper_bound(a[i]);
21         it--;
22         R[a[i]]=*it;
23         f.erase(f.find(a[i+1]));
24         if(index<=n)
25             f.insert(a[index++]);
26     }
27     f.clear();
28     f.insert(0);
29     index=n-1;
30     for(;index>=max(1,n-k);index--)
31         f.insert(a[index]);
32 
33     for(int i=n;i>=1;i--)
34     {
35         auto it=f.upper_bound(a[i]);
36         it--;
37         L[a[i]]=*it;
38         f.erase(f.find(a[i-1]));
39         if(index>=1)
40             f.insert(a[index--]);
41     }
42 //    for(int i=1;i<=n;i++)
43 //        printf("%d R:%d L:%d\n",a[i],R[a[i]],L[a[i]]);
44 
45     s[0]==0;
46     for(int i=1;i<=n;i++)
47     {
48         s[i]=s[max(R[i],L[i])]+1;
49         printf("%d%c",s[i],i==n?'\n':' ');
50     }
51 
52 }
53 int main()
54 {
55     int t;
56     cin>>t;
57     while(t--)
58     {
59         cin>>n>>k;
60         for(int i=1;i<=n;i++)
61             cin>>a[i];
62         Solve();
63     }
64 }
View Code

 

I.Washing clothes(贪心)

•题意

有n个人分别在$t_{i}$时刻进入洗衣房要洗衣服,可以机洗可以手洗,

但是只有一台洗衣机,每次机洗花费的时间是$x$,同时也可以手洗花费时间是$m$,

可以多人同时手洗,而不能同时机洗

求当$x\epsilon [1,y]$,所有人所要花费的最少时间。

•思路

 

 如何判断从哪个人开始使用洗衣机呢?

我们可以使手洗时间尽可能的与机洗时间相同,找到这个点,即为最小值

为什么这个点是最小值呢?

因为$f(i)$是单调递增函数,$g(i)$是单调递减函数

设两者相等时机洗$cnt$个,手洗$n-cnt$个

①现在让手洗多一个,机洗少一个,

  因为$f(i)$单调递增,$f(n-cnt+1)>f(n-cnt)$,所以$max(f(i),g(i))$增大,花费时间增加

②现在让手洗少一个,机洗多一个,

  手洗花费时间减少$f(i)<g(i)$,已经不影响最大值,而机洗时间可能增大,花费时间可能增加

 

如何来计算这一点

可以利用手洗和机洗的时间关系,计算手洗一件可以机洗cnt件,

那后$cnt$件可以机洗,前$n-cnt$件用手洗(手洗早结束也早,所以是在cnt件机洗结束时结束)

 

这里解释一下题解中机洗答案为什么是$max_{j=i}^{N}t_{j}+(n-j+1)*x$

用洗衣机洗有两种情况:

①洗衣机无空闲时间。排队等着前一个人用完洗衣机后一个人接着用

  其中$t_{j}+(n-j+1)*x$其实是相当于把后面的衣服都在$j$洗完以后接着洗的,

  也就是人来了在等洗衣机洗完,洗完接着又开始洗下一个人的衣服,计算无空闲时间情况

②洗衣机有空闲时间。前一个人用完洗衣机,后一个人还没到,隔一段时间才到然后用洗衣机

  从i到n取最大值,其实是假设第$j$个人洗完,而第$j+1$个人还没来

  那第$j$个人肯定不会影响最大值了,第$j$个人的时间肯定在$t[j+1]$之内了,计算有空闲时间情况

 

•代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 const int maxn=1e6+5;
 5 ll a[maxn];
 6 
 7 int main()
 8 {
 9     int n,m;
10     while(~scanf("%d%d",&n,&m))
11     {
12         for(int i=1;i<=n;i++)
13             scanf("%lld",a+i);
14 
15         sort(a+1,a+1+n);
16 
17         for(int i=1;i<=m;i++)
18         {
19             int cnt=min(n,m/i);///手洗一个能机洗的cnt个
20             ///令手洗与机洗结束时间相同
21             ///后cnt个机洗,前n-cnt个手洗
22             ll ans=0;
23             if(cnt<n)
24                 ans=a[n-cnt]+m;///手洗所用时间
25             for(int j=n-cnt+1;j<=n;j++)///机洗
26                 ans=max(ans,a[j]+1ll*(n-j+1)*i);
27 
28             printf("%lld%c",ans,i==m?'\n':' ');
29         }
30     }
31 }
View Code

 

posted @ 2019-09-02 17:02  MMMinoz  阅读(277)  评论(0编辑  收藏  举报