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]<<' ';}
多项式练少了,想不到这方面。