VP abc137
A. +-x
- 模拟题
void solve(){
cin>>a>>b;
cout<<max({a+b,a*b,a-b})<<endl;
}
B. One Clue
- 模拟题
void solve(){
cin>>k>>x;
if(k==1){
cout<<x<<endl;
return;
}
for(int i=x-k+1;i<=x+k-1;i++){
cout<<i<<' ';
}
}
C. Green Bin
-
题意:给出变位词概念,叫我们求出变位词对的个数。
-
解题:变位词本质上就是排完序是同一个单词,然后通过交换其中几个词。题目要求变位词对的个数,我们可以先统计排完序单词的个数,然后按照组合数 $C_{cnt}^{2}$ 来计算最后的答案即可。
void solve(){
cin>>n;
map<string,int>S;
for(int i=1;i<=n;i++){
cin>>s;
sort(s.begin(),s.end());
S[s]++;
}
int ans=0;
for(auto& [x,y]:S)ans+=(y-1)*y/2;
cout<<ans;
}
D. Summer Vacation
-
题意:有 $n$ 个工会,如果你接受第 $i$ 个工作并完成它,你将在完成当天之后的 $A_i$ 天内获得奖励 $B_i$ 并且每天最多只能接受并完成一个工作,同时不能重新接受已经完成过的工作,求不迟于 $M$ 天内能获得的最大奖励数。
-
解题:要求最大奖励数,其实可以想到:尽可能选时间短的奖励多的,因此可以先对 $A_i$ 从小到大排序。那么之后我们肯定是尽可能选奖励多的,因此我们可以开一个大顶堆,把所有截至到第 $i$ 天之前的奖励数全部放入堆中,然后取出最大的即可。这样就能保证我们的贪心是正确的。
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>w[i].x>>w[i].y;
}
sort(w+1,w+1+n);
priority_queue<int>q;
int t=1;
int res=0;
for(int i=1;i<=m;i++){
while(t<=n&&w[t].x<=i){
q.push(w[t++].y);
}
if(q.size()){
res+=q.top();
q.pop();
}
}
cout<<res<<endl;
}
这就是具有二维变量的贪心,有的时候可能是用
dp去做,但本题数据规模较大,因此得想到贪心,这种常见的思路一般是先定一个顺序然后另一个顺序可以用类似 双指针、单调队列、堆 等优化和计算。
E. Coins Respawn
-
题意:给你一张图,你从 $1$ 号点出发,要到 $n$ 号点,图上的每一条边都有硬币,你要收集它们,当然当你收集了一次之后在这条边又会出现这个硬币。最后到 $n$ 号节点时要上交 $T\times P$ 个硬币,其中 $T$ 是走的时间,$P$ 是给定值。求最后获得到的硬币数最多是多少。
-
思路:因为最后都要上交 $T\times P$ 个硬币,那不如简化运算,然所有权值都减去 $P$,这样最后就可以不用上交了。然后我们再考虑什么情况下会无解,也就是答案无限大的情况,那就是有一个边权和为正数的环与点 $n$ 联通,这里就先建立个反图,从 $n$ 出发,标记能走到的点,然后 spfa 的时候不去松弛它们,这样就能保证用spfa判断正环的时候包括 $n$ 这个点了(很重要,要不然过不了第三个测试点)。然后要求最大的答案,此时就是求从 $1\rightarrow n$ 的最长路,注意这里有可能有负权边,因此必须用
spfa。
int n,m,p;
vector<PII>G[N];
vector<int>g[N];
bool vis[N],st[N];
int cnt[N],dist[N];
void dfs(int u){
if(vis[u])return;
vis[u]=1;
for(auto j:g[u])dfs(j);
}
void spfa(){
memset(dist,-0x3f,sizeof dist);
dist[1]=0,st[1]=1;
queue<int>q;
q.push(1);
while(q.size()){
int t=q.front();
q.pop();
st[t]=0;
for(auto [j,w]:G[t]){
if(!vis[j])continue;
if(dist[j]<dist[t]+w){
dist[j]=dist[t]+w;
if(!st[j]){
cnt[j]++;
if(cnt[j]>n){
cout<<"-1"<<endl;
exit(0);
}
q.push(j);
st[j]=1;
}
}
}
}
}
void solve(){
cin>>n>>m>>p;
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
G[a].emplace_back(b,c-p);
g[b].emplace_back(a);
}
dfs(n);
spfa();
cout<<max(dist[n],0ll)<<endl;
}
看似简单,实则细节满满。
F. Polynomial Construction 构造+多项式
-
题意:给出长度为 $p$ 的 $01$ 序列 $a$, 构造 $f(x)=\sum\limits_{i=0}^{p-1} b_i x^i$,满足 $f(i) \equiv a_i \pmod{p}$。
-
思路:可以看出 $f(x)$ 是一个多项式,此时可以考虑二项展开式。又因为此时的 $a$ 只有 $01$ 两种取值,因此可以:在 $a_x = 1$ 时构造一个函数 $g(i)$ 使 $i = x$ 时 $g(i) = 1$,$i \not = x$ 时 $g(i)=0$。这样使求和后只对 $f(x)$ 产生影响。由于题目要求是在模意义下(又因为 $p$ 为质数),且值为 $1$,可以考虑用费马小定理构造出 $g(i) = 1 - (i-x)^{p-1}$,后面的 $(i-x)^{p-1}$ 用二项式定理展开得 $\displaystyle\sum_{j=0}^{p-1} \binom{p-1}{j}i{p-j-1}(-x)i$。
void solve(){
cin>>p;
for(int i=0;i<=p;i++){
for(int j=0;j<=i;j++){
if(!j)C[i][j]=1;
else C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
}
}
for(int i=0;i<p;i++){
cin>>a[i];
if(!a[i])continue;
int f[N]={1},now[N]={};
for(int j=1;j<p;j++)f[j]=(f[j-1]*i)%p;
for(int j=0;j<p;j++){
if((p-j)&1)now[j]=(C[p-1][j]*f[p-1-j])%p;
else now[j]=-(C[p-1][j]*f[p-1-j])%p;
}
for(int j=0;j<p;j++)b[j]=(b[j]-now[j]+p)%p;
b[0]=(b[0]+1)%p;
}
for(int i=0;i<p;i++)cout<<b[i]<<' ';
}
多项式练少了,想不到这方面。

浙公网安备 33010602011771号