【做题记录】杂题乱做
LG P13280 「CZOI-R4」午夜巡游
不难发现除了 \(k\) 之外的其他数都是等价的,于是我们只需要计算 \(k\) 出现了几次即可。
考虑将所有 \(i\in[1,n]\) 连一条 \(i\to p_i\) 的有向边,这张图一定由若干个环组成。而最终答案为 \(k\) 等价于 \(k\) 所在的这个环的长度 \(x\mid m\)。 于是我们枚举 \(x\in[1,n]\land x\mid m\),计算有多少个排列满足 \(k\) 所在的环长度为 \(x\)。不难算出这个数量为 \(\operatorname{A}_{n-1}^{x-1}\times(n-x)!\)。时间复杂度 \(O(T\sqrt{m})\)。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e7+5,mod=998244353;
int T,n,m,kk,fac[maxn],inv[maxn],tot,ans;
il int pls(int x,int y){
return x+y<mod?x+y:x+y-mod;
}
il int sub(int x,int y){
return x<y?x-y+mod:x-y;
}
il void add(int &x,int y){
x=pls(x,y);
}
il void mns(int &x,int y){
x=sub(x,y);
}
il int qpow(int x,int y=mod-2){
int res=1;
while(y){
if(y&1){
res=res*1ll*x%mod;
}
x=x*1ll*x%mod,y>>=1;
}
return res;
}
il int A(int x,int y){
if(x<y||y<0){
return 0;
}
// cout<<x<<' '<<x-y<<'\n';
return fac[x]*1ll*inv[x-y]%mod;
}
il int C(int x,int y){
if(x<y||y<0){
return 0;
}
return fac[x]*1ll*inv[y]%mod*inv[x-y]%mod;
}
il void init(int n=1e7){
fac[0]=1;
for(int i=1;i<=n;i++){
fac[i]=fac[i-1]*1ll*i%mod;
}
inv[n]=qpow(fac[n]);
for(int i=n;i;i--){
inv[i-1]=inv[i]*1ll*i%mod;
}
}
il void solve(int x){
if(x>n){
return ;
}
int num=A(n-1,x-1)*1ll*fac[n-x]%mod;
mns(tot,num),add(ans,num*1ll*kk%mod);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
init();
cin>>T;
while(T--){
cin>>n>>m>>kk;
if(!m){
cout<<fac[n]*1ll*kk%mod<<'\n';
continue;
}
ans=0,tot=fac[n];
for(int i=1;i<=m/i;i++){
if(m%i==0){
solve(i);
if(i!=m/i){
solve(m/i);
}
}
}
add(ans,(n*1ll*(n+1)/2-kk)%mod*tot%mod*qpow(n-1)%mod);
cout<<ans<<'\n';
}
return 0;
}
}
int main(){return asbt::main();}
LG P8880 无知时诋毁原神
首先发现这个排列是诈骗,记录每个值的位置即可。打个表出来,发现 \(n\) 为偶数时无解,\(n\) 为奇数时对于 \(c=\{0,1,2,\dots,n-1\}\),\(\begin{cases}\begin{aligned}a&=\{0,n-1,n-2,n-3,\dots,1\}\\b&=\{0,2,4,6,\dots,n-1,1,3,5,\dots,n-2\}\end{aligned}\end{cases}\) 为一组可行解。这组可行解的正确性显然,考虑偶数无解的证明。虽然此时你已经完全可以写出 AC 代码了
排列 \(c\) 中的数之和为 \(\frac{n(n-1)}{2}\),当 \(n\) 为偶数时,\(n-1\) 为奇数,于是显然有 \(n\nmid \frac{n(n-1)}{2}\)。而排列 \(a\) 与 \(b\) 中的数之和为 \(n(n-1)\),显然 \(n\mid n(n-1)\)。于是 \(\sum a+\sum b\equiv 0\not\equiv\sum c\pmod{n}\),于是无解。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n,c[maxn],a[maxn],b[maxn];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
if(n%2==0){
cout<<-1;
return 0;
}
for(int i=0,x;i<n;i++){
cin>>x;
c[x]=i;
}
for(int i=1,j=2;i<n;i++){
a[c[i]]=n-i;
b[c[i]]=j,j=(j+2)%n;
}
for(int i=0;i<n;i++){
cout<<a[i]<<' ';
}
cout<<'\n';
for(int i=0;i<n;i++){
cout<<b[i]<<' ';
}
return 0;
}
}
int main(){return asbt::main();}
LG P10655 [ROI 2017] 反物质 (Day 2)
设 \(dp_i\) 表示当前有 \(i\) 克反物质,未来最坏情况下最多获得的利润加 \(i\times10^9\) 的值,转移方程是 \(dp_i=\max(i\times10^9,\max\limits_{j\,\operatorname{s.t.}i+r_j\le a}\{\min_{k=i+l_j}^{i+r_j}\{dp_k-c_j\}\})\)。相当于是倒着 DP,在每次实验的开头再计算成本。
时间复杂度 \(O(na^2)\) 无法接受,考虑优化。一个直接的思路是开 \(n\) 个单调队列,但这样空间太大。那么我们不妨维护每个实验对应的最小值的位置。具体地,用单调栈维护 \(L_i\) 表示 \(i\) 左侧第一个 \(dp\) 值比 \(dp_i\) 小的位置,转移时寻找对于实验 \(j\) 可转移的最小的 \(dp\) 值的位置即可。于是时间复杂度 \(O(na)\)。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=2e6+5;
int n,m,ll[105],rr[105],cc[105],pos[105];
int dp[maxn],stk[maxn],top,L[maxn];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>ll[i]>>rr[i]>>cc[i];
pos[i]=m+1;
}
for(int i=m;~i;i--){
dp[i]=i*1e9;
for(int j=1;j<=n;j++){
pos[j]=min(pos[j],i+rr[j]);
while(L[pos[j]]>=i+ll[j]){
pos[j]=L[pos[j]];
}
dp[i]=max(dp[i],dp[pos[j]]-cc[j]);
}
while(top&&dp[stk[top]]>dp[i]){
L[stk[top--]]=i;
}
stk[++top]=i;
}
cout<<dp[0];
return 0;
}
}
signed main(){return asbt::main();}
UVA10900 你想当2^n元富翁吗? So you want to be a 2n-aire?
概期考虑倒着 DP,设 \(f_i\) 表示答对第 \(i\) 题后的最大期望,假设答对第 \(i+1\) 道题的概率为 \(p\),于是 \(f_i=\max(2^i,p\times f_{i+1})\)。但是 \(p\) 不是固定的,于是我们需要对 \(2^i\) 和 \(p\times f_{i+1}\) 进行分类讨论。具体地,设 \(p_0=\frac{2^i}{f_{i+1}}\),那么如果 \(p<p_0\),选择 \(2^i\) 更优,概率为 \(\frac{p_0-t}{1-t}\);否则选择 \(p\times f_{i+1}\) 更优,\(p\) 取平均值 \(\frac{1+p_0}{2}\),概率为 \(\frac{1-p_0}{1-t}\)。所以我们得到了最终的转移方程:
初始状态 \(f_n=2^n\),答案即为 \(f_0\)。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
int n;
double m,f[39];
int main(){
while(~scanf("%d%lf",&n,&m)&&n){
f[n]=1<<n;
for(int i=n;i;i--){
double p0=max(m,(1<<(i-1))*1.0/f[i]);
f[i-1]=(p0-m)/(1-m)*(1<<(i-1))+(1-p0)*(1+p0)/2/(1-m)*f[i];
}
printf("%.3f\n",f[0]);
}
return 0;
}
}
int main(){return asbt::main();}
UVA297 四分树 Quadtrees
同时 dfs 两棵树,黑点覆盖灰点和白点,灰点覆盖白点,在相应层累加答案即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int num[]={1024,256,64,16,4,1};
int T,ans,ca,cb;
string a,b;
il void dfs(bool fa,bool fb,bool fr,int d){
// cout<<ca<<' '<<a[ca]<<' '<<cb<<' '<<b[cb]<<' '<<fa<<' '<<fb<<' '<<fr<<' '<<d<<'\n';
if(!fb){
char cha=a[ca++];
if(cha=='f'){
if(fr){
ans+=num[d];
}
}
else if(cha=='p'){
dfs(fa,fb,fr,d+1);
dfs(fa,fb,fr,d+1);
dfs(fa,fb,fr,d+1);
dfs(fa,fb,fr,d+1);
}
}
else if(!fa){
char chb=b[cb++];
if(chb=='f'){
if(fr){
ans+=num[d];
}
}
else if(chb=='p'){
dfs(fa,fb,fr,d+1);
dfs(fa,fb,fr,d+1);
dfs(fa,fb,fr,d+1);
dfs(fa,fb,fr,d+1);
}
}
else{
char cha=a[ca++],chb=b[cb++];
if(cha=='f'){
ans+=num[d];
if(chb=='p'){
dfs(0,1,0,d+1);
dfs(0,1,0,d+1);
dfs(0,1,0,d+1);
dfs(0,1,0,d+1);
}
}
else if(cha=='p'){
if(chb=='f'){
ans+=num[d];
dfs(1,0,0,d+1);
dfs(1,0,0,d+1);
dfs(1,0,0,d+1);
dfs(1,0,0,d+1);
}
else if(chb=='p'){
dfs(1,1,1,d+1);
dfs(1,1,1,d+1);
dfs(1,1,1,d+1);
dfs(1,1,1,d+1);
}
else{
dfs(1,0,1,d+1);
dfs(1,0,1,d+1);
dfs(1,0,1,d+1);
dfs(1,0,1,d+1);
}
}
else{
if(chb=='f'){
ans+=num[d];
}
else if(chb=='p'){
dfs(0,1,1,d+1);
dfs(0,1,1,d+1);
dfs(0,1,1,d+1);
dfs(0,1,1,d+1);
}
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>T;
while(T--){
cin>>a>>b;
ca=cb=ans=0;
dfs(1,1,1,0);
// cout<<a.size()<<' '<<b.size()<<' '<<ca<<' '<<cb<<'\n';
cout<<"There are "<<ans<<" black pixels.\n";
}
return 0;
}
}
int main(){return asbt::main();}
LG P10184 [YDOI R1] whk
显然答案可以二分。在 \(x\) 天中我们只需要做 \(x\times t\) 道题就可以了。如果某个科目 \(a_i\ge x\),那么它只能贡献 \(x\) 道题;否则可以贡献 \(a_i\) 道题。时间复杂度线性对数。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=5e5+5;
int n,m,a[maxn];
il bool check(int x){
int tot=0;
for(int i=1;i<=n;i++){
tot+=min(a[i],x);
}
return tot>=x*m;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int l=0,r=5e11;
while(l<r){
int mid=(l+r+1)>>1;
if(check(mid)){
l=mid;
}
else{
r=mid-1;
}
}
cout<<l;
return 0;
}
}
signed main(){return asbt::main();}
AT_dp_t Permutation
考虑设 \(f_{i,j}\) 表示在第 \(i\) 个位置填 \(j\) 的方案数,但是我们不确定当前填入了哪些数,难以转移。于是增加限制,强制此时填入的是 \(1\) 到 \(i\) 中的数。对于在 \(i+1\) 填 \(j\) 的情况,我们可以将前 \(i\) 个数中 \(\ge j\) 的数全部加一以保证不漏情况。于是可以转移:
前缀和优化即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=3e3+5,mod=1e9+7;
il int pls(int x,int y){
return x+y<mod?x+y:x+y-mod;
}
il int sub(int x,int y){
return x<y?x-y+mod:x-y;
}
int n,f[maxn][maxn],g[maxn][maxn];
string s;
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>s;
s=" "+s;
f[1][1]=g[1][1]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
if(s[i-1]=='<'){
f[i][j]=g[i-1][j-1];
}
else{
f[i][j]=sub(g[i-1][i-1],g[i-1][j-1]);
}
g[i][j]=pls(g[i][j-1],f[i][j]);
}
}
cout<<g[n][n];
return 0;
}
}
int main(){return asbt::main();}
LG P6862 [RC-03] 随机树生成器
总数等于总方案数乘期望,于是我们求期望即可。对于 \(i\),所有 \(j>i\) 都会产生 \(\frac{1}{j-1}\) 的贡献,预处理出逆元的前缀和即可 \(O(1)\) 求解。别忘了加上父亲的贡献。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e7+5,mod=1e9+9;
int T,n,m,fac[maxn],inv[maxn];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
fac[0]=fac[1]=inv[1]=1;
for(int i=2;i<=1e7;i++){
fac[i]=fac[i-1]*1ll*i%mod;
inv[i]=inv[mod%i]*1ll*(mod-mod/i)%mod;
}
for(int i=1;i<=1e7;i++){
(inv[i]+=inv[i-1])%=mod;
}
cin>>T;
while(T--){
cin>>n>>m;
cout<<(inv[n-1]-inv[m-1]+mod+(m>1))*1ll*fac[n-1]%mod<<'\n';
}
return 0;
}
}
int main(){return asbt::main();}
CF1608E The Cells on the Paper
显然答案可二分,考虑如何 \(check\)。考虑三个矩形的分布,不难发现只有以下几种:

而三种颜色有六种排列,总共就有三十六种情况。只需考虑其中的两种,其它的可以旋转同理。

从左往右,先满足红色数量 \(\ge mid\) 个,然后满足绿色,最后检验蓝色。

先从下往上满足蓝色 \(\ge mid\) 个,然后在上面的区域从左往右满足红色,最后检验绿色。
时间复杂度 \(O(36n\log n)\)。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define lwrb lower_bound
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n,cnt[3],lsx[maxn],tx,lsy[maxn],ty,cx[3][maxn],cy[3][maxn];
struct node{
int xx,yy;
}a[3][maxn],b[3][maxn];
il bool cmp1(node x,node y){
return x.xx<y.xx;
}
il bool cmp2(node x,node y){
return x.yy<y.yy;
}
il bool check(int kk){
// 1
for(int x:{0,1,2}){
int i=1,cnt=0;
for(;i<=ty;i++){
cnt+=cy[x][i];
if(cnt>=kk){
goto togo11;
}
}
continue;
togo11:;
for(int y:{0,1,2}){
if(x==y){
continue;
}
int j=i+1;
for(cnt=0;j<=ty;j++){
cnt+=cy[y][j];
if(cnt>=kk){
goto togo12;
}
}
continue;
togo12:;
for(int z:{0,1,2}){
if(x==z||y==z){
continue;
}
int k=j+1;
for(cnt=0;k<=ty;k++){
cnt+=cy[z][k];
if(cnt>=kk){
return 1;
}
}
}
}
}
// 2
for(int x:{0,1,2}){
int i=1,cnt=0;
for(;i<=tx;i++){
cnt+=cx[x][i];
if(cnt>=kk){
goto togo21;
}
}
continue;
togo21:;
for(int y:{0,1,2}){
if(x==y){
continue;
}
int j=i+1;
for(cnt=0;j<=tx;j++){
cnt+=cx[y][j];
if(cnt>=kk){
goto togo22;
}
}
continue;
togo22:;
for(int z:{0,1,2}){
if(x==z||y==z){
continue;
}
int k=j+1;
for(cnt=0;k<=tx;k++){
cnt+=cx[z][k];
if(cnt>=kk){
return 1;
}
}
}
}
}
// 3
for(int x:{0,1,2}){
int i=1,cnt=0;
for(;i<=ty;i++){
cnt+=cy[x][i];
if(cnt>=kk){
goto togo31;
}
}
continue;
togo31:;
for(int y:{0,1,2}){
if(x==y){
continue;
}
int j=0;cnt=0;
for(int k=1;k<=n;k++){
if(a[y][k].yy>i){
if(++cnt==kk){
j=a[y][k].xx;
goto togo32;
}
}
}
continue;
togo32:;
for(int z:{0,1,2}){
if(x==z||y==z){
continue;
}
cnt=0;
for(int k=n;k&&a[z][k].xx>j;k--){
if(a[z][k].yy>i){
if(++cnt==kk){
return 1;
}
}
}
}
}
}
// 4
for(int x:{0,1,2}){
int i=ty,cnt=0;
for(;i;i--){
cnt+=cy[x][i];
if(cnt>=kk){
goto togo41;
}
}
continue;
togo41:;
for(int y:{0,1,2}){
if(x==y){
continue;
}
int j=0;cnt=0;
for(int k=1;k<=n;k++){
if(a[y][k].yy<i){
if(++cnt==kk){
j=a[y][k].xx;
goto togo42;
}
}
}
continue;
togo42:;
for(int z:{0,1,2}){
if(x==z||y==z){
continue;
}
cnt=0;
for(int k=n;k&&a[z][k].xx>j;k--){
if(a[z][k].yy<i){
if(++cnt==kk){
return 1;
}
}
}
}
}
}
// 5
for(int x:{0,1,2}){
int i=1,cnt=0;
for(;i<=tx;i++){
cnt+=cx[x][i];
if(cnt>=kk){
goto togo51;
}
}
continue;
togo51:;
for(int y:{0,1,2}){
if(x==y){
continue;
}
int j=0;cnt=0;
for(int k=1;k<=n;k++){
if(b[y][k].xx>i){
if(++cnt==kk){
j=b[y][k].yy;
goto togo52;
}
}
}
continue;
togo52:;
for(int z:{0,1,2}){
if(x==z||y==z){
continue;
}
cnt=0;
for(int k=n;k&&b[z][k].yy>j;k--){
if(b[z][k].xx>i){
if(++cnt==kk){
return 1;
}
}
}
}
}
}
// 6
for(int x:{0,1,2}){
int i=tx,cnt=0;
for(;i;i--){
cnt+=cx[x][i];
if(cnt>=kk){
goto togo61;
}
}
continue;
togo61:;
for(int y:{0,1,2}){
if(x==y){
continue;
}
int j=0;cnt=0;
for(int k=1;k<=n;k++){
if(b[y][k].xx<i){
if(++cnt==kk){
j=b[y][k].yy;
goto togo62;
}
}
}
continue;
togo62:;
for(int z:{0,1,2}){
if(x==z||y==z){
continue;
}
cnt=0;
for(int k=n;k&&b[z][k].yy>j;k--){
if(b[z][k].xx<i){
if(++cnt==kk){
return 1;
}
}
}
}
}
}
return 0;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1,x,y,z;i<=n;i++){
cin>>x>>y>>z;
lsx[++tx]=x,lsy[++ty]=y,z--;
a[z][++cnt[z]]={x,y};
}
n/=3;
sort(lsx+1,lsx+tx+1);
tx=unique(lsx+1,lsx+tx+1)-lsx-1;
sort(lsy+1,lsy+ty+1);
ty=unique(lsy+1,lsy+ty+1)-lsy-1;
for(int i=1;i<=n;i++){
for(int j:{0,1,2}){
a[j][i].xx=lwrb(lsx+1,lsx+tx+1,a[j][i].xx)-lsx;
a[j][i].yy=lwrb(lsy+1,lsy+ty+1,a[j][i].yy)-lsy;
cx[j][a[j][i].xx]++,cy[j][a[j][i].yy]++;
b[j][i]=a[j][i];
}
}
for(int i:{0,1,2}){
sort(a[i]+1,a[i]+n+1,cmp1);
sort(b[i]+1,b[i]+n+1,cmp2);
}
int l=1,r=n;
while(l<r){
int mid=(l+r+1)>>1;
if(check(mid)){
l=mid;
}
else{
r=mid-1;
}
}
cout<<l*3;
return 0;
}
}
int main(){return asbt::main();}
LG P3174 [HAOI2009] 毛毛虫
treedp,分别计算子树中以根为端点和穿过根的最长的毛毛虫长度,转移时记录最大值和次大值即可,不要忘了穿过 \(u\) 时 \(fa_u\) 也可以算在毛毛虫上。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=3e5+5;
int n,m,ans,f[maxn],g[maxn];
vector<int> e[maxn];
il void dfs(int u,int fa){
int mx[3]={},son=0;
for(int v:e[u]){
if(v==fa){
continue;
}
son++,dfs(v,u),mx[2]=g[v];
sort(mx,mx+3,greater<int>());
}
switch(son){
case 0:
case 1:g[u]=mx[0]+1,f[u]=mx[0]+(fa?2:1);break;
default:g[u]=mx[0]+son,f[u]=mx[0]+mx[1]+son-(fa?0:1);break;
}
ans=max(ans,f[u]);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1,u,v;i<n;i++){
cin>>u>>v;
e[u].pb(v),e[v].pb(u);
}
dfs(1,0);
cout<<ans;
return 0;
}
}
int main(){return asbt::main();}
UVA557 汉堡 Burger
正难则反,考虑最后剩下两个不同的汉堡的概率,即前面 \(n-2\) 个位置每种汉堡正好有 \(\frac{n}{2}-1\) 个,而每次选择的概率都是 \(\frac{1}{2}\),于是概率即为:
但是有多测,考虑预处理。记上面那个式子为 \(f_n\),显然 \(n\) 为偶数,于是考虑从 \(f_{n-2}\) 递推。有:
于是可得 \(f_n=\frac{n-3}{n-2}f_{n-2}\)。线性预处理,\(O(1)\) 回答即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int T,n;
double f[maxn];
int main(){
f[2]=1;
for(int i=4;i<=1e5;i+=2){
f[i]=f[i-2]/(i-2)*(i-3);
}
scanf("%d",&T);
while(T--){
scanf("%d",&n);
printf("%.4f\n",1-f[n]);
}
return 0;
}
}
int main(){return asbt::main();}
CodeChef - SOD3 Yet another SOD problem
差分后数位 DP 即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
int T,dig[21];
ll L,R,dp[21][3];
il ll dfs(int i,int j,bool limit){
if(!limit&&~dp[i][j]){
return dp[i][j];
}
if(i==1){
int mx=limit?dig[1]:9,res=0;
for(int k=0;k<=mx;k++){
if(k%3==j){
res++;
}
}
if(!limit){
dp[i][j]=res;
}
return res;
}
int mx=limit?dig[i]:9;
ll res=0;
for(int k=0;k<=mx;k++){
res+=dfs(i-1,(j-k+9)%3,limit&&k==mx);
}
if(!limit){
dp[i][j]=res;
}
return res;
}
il ll solve(ll x){
int cnt=0;
do{
dig[++cnt]=x%10,x/=10;
}while(x);
return dfs(cnt,0,1);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
memset(dp,-1,sizeof(dp));
cin>>T;
while(T--){
cin>>L>>R;
cout<<solve(R)-solve(L-1)<<'\n';
}
return 0;
}
}
int main(){return asbt::main();}
SP39 PIGBANK - Piggy-Bank
直接跑完全背包即可。UVA 和 SPOJ 的绿题都好水
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int inf=1e9;
int T,n,m,a[505],b[505],dp[10005];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>T;
while(T--){
cin>>n>>m,m-=n,cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i];
}
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;i++){
for(int j=b[i];j<=m;j++){
dp[j]=min(dp[j],dp[j-b[i]]+a[i]);
}
}
if(dp[m]>=inf){
cout<<"This is impossible.\n";
}
else{
cout<<"The minimum amount of money in the piggy-bank is "<<dp[m]<<".\n";
}
}
return 0;
}
}
int main(){return asbt::main();}
SP57 SUPPER - Supernumbers in a permutation
用树状数组维护出每个数为结尾、开头的 LIS,与答案比较即可。时间复杂度线性对数。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n,a[maxn],pre[maxn],suf[maxn];
vector<int> ans;
struct{
int tr[maxn];
#define lowbit(x) (x&-x)
il void init(){
for(int i=1;i<=n;i++){
tr[i]=0;
}
}
il void upd(int p,int v){
for(;p<=n;p+=lowbit(p)){
tr[p]=max(tr[p],v);
}
}
il int query(int p){
int res=0;
for(;p;p-=lowbit(p)){
res=max(res,tr[p]);
}
return res;
}
#undef lowbit
}F;
int main(){
ios::sync_with_stdio(0),cin.tie(0);
while(cin>>n){
for(int i=1;i<=n;i++){
cin>>a[i];
}
int len=0;
F.init();
for(int i=1;i<=n;i++){
pre[a[i]]=F.query(a[i]);
F.upd(a[i],pre[a[i]]+1);
len=max(len,pre[a[i]]+1);
}
F.init();
for(int i=n;i;i--){
suf[a[i]]=F.query(n-a[i]+1);
F.upd(n-a[i]+1,suf[a[i]]+1);
}
ans.clear();
for(int i=1;i<=n;i++){
if(pre[i]+suf[i]+1==len){
ans.pb(i);
}
}
cout<<ans.size()<<'\n';
for(int i:ans){
cout<<i<<' ';
}
cout<<'\n';
}
return 0;
}
}
int main(){return asbt::main();}
CF949E Binary Cards
首先可以发现对于每个 \(k\),只选 \(2^k\) 或只选 \(-2^k\) 或二者都不选一定是最优的。于是考虑每一位,如果当前所有数这一位有 \(1\),那么我们就需要一个 \(2^k\) 或一个 \(-2^k\),然后将所有数除以 \(2\) 递归。爆搜看似过不去,但是每次递归后值域都会减少一半,去重即可。时空都是线性对数的。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n;
vector<int> a;
il vector<int> dfs(int dep){
// cout<<a[0]<<'\n';
a.erase(unique(a.begin(),a.end()),a.end());
for(auto i=a.begin();i<a.end();i++){
// cout<<*i<<' ';
if(*i==0){
a.erase(i);
break;
}
}
// cout<<'\n';
if(a.empty()){
// puts("666");
return vector<int>();
}
if(a.size()==1){
if(a[0]==1){
return vector<int>(1,1<<dep);
}
if(a[0]==-1){
return vector<int>(1,-1<<dep);
}
}
// if(a[0]==0){
// puts("NB");
// cout<<a.size()<<'\n';
// }
for(int x:a){
if(x&1){
goto togo;
}
}
for(int &x:a){
x>>=1;
}
return dfs(dep+1);
togo:;
vector<int> b=a;
for(int &x:a){
if(x&1){
x--;
}
x>>=1;
}
vector<int> ra=dfs(dep+1);
ra.pb(1<<dep);
a=b;
for(int &x:a){
if(x&1){
x++;
}
x>>=1;
}
vector<int> rb=dfs(dep+1);
rb.pb(-1<<dep);
return ra.size()<rb.size()?ra:rb;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1,x;i<=n;i++){
cin>>x,a.pb(x);
}
sort(a.begin(),a.end());
vector<int> ans=dfs(0);
cout<<ans.size()<<'\n';
for(int x:ans){
cout<<x<<' ';
}
return 0;
}
}
int main(){return asbt::main();}
AT_dp_y Grid 2
设 \(dp_i\) 表示走到第 \(i\) 个障碍物、不经过其他障碍物的方案数,容斥即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=3e3+5,maxm=2e5+5,mod=1e9+7;
int n,m,kk,fac[maxm],inv[maxm],dp[maxn];
struct node{
int x,y;
il bool operator<(const node &b)const{
return x<b.x||x==b.x&&y<b.y;
}
}a[maxn];
il void init(int n=2e5){
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=2;i<=n;i++){
fac[i]=fac[i-1]*1ll*i%mod;
inv[i]=(mod-mod/i)*1ll*inv[mod%i]%mod;
}
for(int i=2;i<=n;i++){
inv[i]=inv[i]*1ll*inv[i-1]%mod;
}
}
il int C(int x,int y){
return fac[x]*1ll*inv[y]%mod*inv[x-y]%mod;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m>>kk;
for(int i=1;i<=kk;i++){
cin>>a[i].x>>a[i].y;
}
sort(a+1,a+kk+1);
if(a[kk].x!=n||a[kk].y!=m){
a[++kk]={n,m};
}
init();
for(int i=1,x,y;i<=kk;i++){
x=a[i].x,y=a[i].y;
dp[i]=C(x+y-2,x-1);
for(int j=1;j<i;j++){
if(a[j].y<=y){
dp[i]=(dp[i]-dp[j]*1ll*C(x-a[j].x+y-a[j].y,x-a[j].x)%mod+mod)%mod;
}
}
}
cout<<dp[kk];
return 0;
}
}
int main(){return asbt::main();}
SP6779 GSS7 - Can you answer these queries VII
树剖 + 线段树维护即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n,m,a[maxn],dfn[maxn],cnt,sz[maxn],stk[maxn];
int hes[maxn],top[maxn],dep[maxn],fa[maxn];
int tag[maxn<<2];
bool fp[maxn<<2];
vector<int> e[maxn];
il void dfs1(int u,int fth){
fa[u]=fth,sz[u]=1;
dep[u]=dep[fth]+1;
int mxs=0;
for(int v:e[u]){
if(v==fth){
continue;
}
dfs1(v,u);
sz[u]+=sz[v];
if(mxs<sz[v]){
mxs=sz[v],hes[u]=v;
}
}
}
il void dfs2(int u){
dfn[u]=++cnt,stk[cnt]=u;
if(!top[u]){
top[u]=u;
}
if(hes[u]){
top[hes[u]]=top[u];
dfs2(hes[u]);
}
for(int v:e[u]){
if(v==fa[u]||v==hes[u]){
continue;
}
dfs2(v);
}
}
struct node{
int sum,lm,rm,mm;
node(int sum=0,int lm=0,int rm=0,int mm=0):sum(sum),lm(lm),rm(rm),mm(mm){}
il node operator+(const node &x)const{
return node(sum+x.sum,max(lm,sum+x.lm),max(x.rm,rm+x.sum),max({mm,x.mm,rm+x.lm}));
}
}tr[maxn<<2];
il void pushup(int id){
tr[id]=tr[lid]+tr[rid];
}
il void pushtag(int id,int l,int r,int v){
fp[id]=1,tag[id]=v;
if(v>=0){
tr[id]=node(v*(r-l+1),v*(r-l+1),v*(r-l+1),v*(r-l+1));
}
else{
tr[id]=node(v*(r-l+1),v,v,v);
}
}
il void pushdown(int id,int l,int r){
if(fp[id]){
int mid=(l+r)>>1;
pushtag(lid,l,mid,tag[id]);
pushtag(rid,mid+1,r,tag[id]);
fp[id]=0;
}
}
il void build(int id,int l,int r){
if(l==r){
tr[id]=node(a[stk[l]],a[stk[l]],a[stk[l]],a[stk[l]]);
return ;
}
int mid=(l+r)>>1;
build(lid,l,mid);
build(rid,mid+1,r);
pushup(id);
}
il void upd(int id,int L,int R,int l,int r,int v){
if(L>=l&&R<=r){
pushtag(id,L,R,v);
return ;
}
pushdown(id,L,R);
int mid=(L+R)>>1;
if(l<=mid){
upd(lid,L,mid,l,r,v);
}
if(r>mid){
upd(rid,mid+1,R,l,r,v);
}
pushup(id);
}
il node query(int id,int L,int R,int l,int r){
if(L>=l&&R<=r){
return tr[id];
}
pushdown(id,L,R);
int mid=(L+R)>>1;
if(r<=mid){
return query(lid,L,mid,l,r);
}
if(l>mid){
return query(rid,mid+1,R,l,r);
}
return query(lid,L,mid,l,r)+query(rid,mid+1,R,l,r);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1,u,v;i<n;i++){
cin>>u>>v;
e[u].pb(v),e[v].pb(u);
}
dfs1(1,0),dfs2(1);
build(1,1,n);
cin>>m;
while(m--){
int opt,u,v,c;
cin>>opt>>u>>v;
if(opt==1){
node resu,resv;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
resv=query(1,1,n,dfn[top[v]],dfn[v])+resv;
v=fa[top[v]];
}
else{
resu=query(1,1,n,dfn[top[u]],dfn[u])+resu;
u=fa[top[u]];
}
}
if(dep[u]<dep[v]){
resv=query(1,1,n,dfn[u],dfn[v])+resv;
}
else{
resu=query(1,1,n,dfn[v],dfn[u])+resu;
}
cout<<max({resu.mm,resv.mm,resu.lm+resv.lm})<<'\n';
}
else{
cin>>c;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
}
upd(1,1,n,dfn[top[u]],dfn[u],c);
u=fa[top[u]];
}
if(dep[u]>dep[v]){
swap(u,v);
}
upd(1,1,n,dfn[u],dfn[v],c);
}
}
return 0;
}
}
int main(){return asbt::main();}
LG P4626 一道水题 II
我们要求的,实际上是这个式子:
其中 \(p_k\) 是第 \(k\) 个质数。注意到当 \(p_k>\sqrt{n}\) 时,\(\lfloor\log_{p_k}n\rfloor=1\)。将 \(10^8\) 内的质数全都筛出来不是很可能,考虑我们能接受怎样的复杂度。不妨按块长 \(B=10^6\) 分块,于是从第 \(2\) 块开始的质数的指数都为 \(1\)。于是我们给后面这些块做一个前缀积,打表打出来。对于 \(n\),首先暴力算出 \(1\sim10^6\) 的贡献,然后整块查表,散块区间筛即可。
打表:
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e6+5,mod=1e8+7,B=1e6;
int prm[maxn],prn;
bool npr[maxn];
il void esieve(int n=1e6){
for(int i=2;i<=n;i++){
if(!npr[i]){
prm[++prn]=i;
for(int j=i*i;j<=n;j+=i){
npr[j]=1;
}
}
}
}
int main(){
freopen("P4626.txt","w",stdout);
ios::sync_with_stdio(0),cin.tie(0);
esieve();
cout<<"1,1";
int ans=1;
for(int i=1;i<100;i++){
for(int j=1;j<=B;j++){
npr[j]=0;
}
int st=i*B+1,ed=(i+1)*B;
for(int j=1;j<=prn;j++){
for(int k=max((st+prm[j]-1)/prm[j],prm[j])*prm[j];k<=ed;k+=prm[j]){
npr[k-i*B]=1;
}
}
for(int j=1;j<=B;j++){
if(!npr[j]){
ans=ans*1ll*(j+i*B)%mod;
}
}
cout<<','<<ans;
}
return 0;
}
}
signed main(){return asbt::main();}
AC Code:
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e6+5,mod=1e8+7,B=1e6;
const int pre[]={1,1,4279041,78522927,97275812,6626498,90103459,19755829,89404363,31420837,78259116,79495350,63384896,76365496,77671792,60888616,14364168,69179499,73237343,47511561,98202707,9638787,8923490,19377161,14025453,83914297,99211028,91698985,11861010,41728948,85011248,2957451,96835040,13279739,65389333,35674577,69539515,43558531,90167843,65612508,96727630,84411969,10432978,81144826,76822534,44673268,48780356,42026053,71419640,925833,75468296,56122076,11730540,24214526,57670707,87561236,60795551,16258057,38969193,93135220,99449198,86687779,48716439,54282180,9747238,6014664,88132693,90624798,78394444,54259153,2424434,71634841,6763549,80339105,74090298,10910559,39590831,57807866,78084208,54389248,4052287,74604335,99190428,89368783,73470487,32097770,29222100,30538785,3048397,89311543,48798411,36895907,88691881,24323948,4724247,97882710,84490696,93965490,66087028,92642640,55440368};
int n,prm[maxn],prn;
bool npr[maxn];
il void esieve(int n=1e6){
for(int i=2;i<=n;i++){
if(!npr[i]){
prm[++prn]=i;
for(int j=i*i;j<=n;j+=i){
npr[j]=1;
}
}
}
}
il int calc(int x){
int res=1;
while(res*x<=n){
res*=x;
}
return res;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
esieve();
int ans=1;
if(n<=B){
for(int i=1;i<=prn&&prm[i]<=n;i++){
(ans*=calc(prm[i]))%=mod;
}
}else{
for(int i=1;i<=prn;i++){
(ans*=calc(prm[i]))%=mod;
}
int id=n/B,st=n/B*B+1,ed=n;
(ans*=pre[n/B])%=mod;
for(int i=1;i<=B;i++){
npr[i]=0;
}
for(int i=1;i<=prn;i++){
for(int k=max((st+prm[i]-1)/prm[i],prm[i])*prm[i];k<=ed;k+=prm[i]){
npr[k-id*B]=1;
}
}
for(int i=st;i<=ed;i++){
if(!npr[i-id*B]){
(ans*=i)%=mod;
}
}
}
cout<<ans;
return 0;
}
}
signed main(){return asbt::main();}
UVA930 Polynomial Roots
对于每个根做大除法降幂,然后二次方程求根即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int T,n;
double A[maxn],B[maxn];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>T;
while(T--){
cin>>n;
for(int i=n;~i;i--){
cin>>A[i];
}
for(int i=1,n0=n;i<=n-2;i++){
double x;
cin>>x;
for(int j=n0-1;~j;j--){
B[j]=A[j+1];
A[j]+=A[j+1]*x;
}
n0--;
for(int j=0;j<=n0;j++){
A[j]=B[j];
}
}
double a=A[2],b=A[1],c=A[0];
double x1=(-b+sqrt(b*b-4*a*c))/2/a,x2=(-b-sqrt(b*b-4*a*c))/2/a;
cout<<fixed<<setprecision(1)<<max(x1,x2)<<'\n'<<min(x1,x2)<<'\n';
}
return 0;
}
}
int main(){return asbt::main();}
UVA970 Particles
区间可行性 DP 即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
int T,n;
bool f[105][105][3];
string s;
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>T;
while(T--){
string s;
cin>>s;
n=s.size(),s=" "+s;
for(int i=1;i<=n;i++){
f[i][i][0]=f[i][i][1]=f[i][i][2]=0;
f[i][i][s[i]-'X']=1;
}
for(int len=2;len<=n;len++){
for(int l=1,r=len;r<=n;l++,r++){
f[l][r][0]=f[l][r][1]=f[l][r][2]=0;
for(int p=l;p<r;p++){
f[l][r][0]|=f[l][p][0]&&f[p+1][r][1]||f[l][p][1]&&f[p+1][r][0]||f[l][p][2]&&f[p+1][r][2];
f[l][r][1]|=f[l][p][0]&&f[p+1][r][0]||f[l][p][1]&&f[p+1][r][1]||f[l][p][1]&&f[p+1][r][2]||f[l][p][2]&&f[p+1][r][1];
f[l][r][2]|=f[l][p][0]&&f[p+1][r][2]||f[l][p][2]&&f[p+1][r][0];
}
}
}
cout<<(f[1][n][2]?'Z':(f[1][n][1]?'Y':'X'))<<'\n';
}
return 0;
}
}
int main(){return asbt::main();}
UVA177 折纸痕 Paper Folding
每次相当于将图上的每个点绕上一张图的终点顺时针旋转 90°,起点旋转后的点即为这一张图的终点。对每一条边建三个点(两个会导致整张图连成一坨),可以直接模拟。然后再从点阵还原边即可。注意 \(n=1\) 时不应该输出开头的空格,特判一下。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
int n;
struct{
int L,R,U,D;
int sx,sy,tx,ty;
bool vis[400][400];
}a[15];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
a[1].sx=200,a[1].sy=200;
a[1].tx=202,a[1].ty=202;
a[1].L=200,a[1].R=202,a[1].U=202,a[1].D=200;
a[1].vis[200][200]=a[1].vis[201][200]=a[1].vis[202][200]=a[1].vis[202][201]=a[1].vis[202][202]=1;
for(int p=2;p<=13;p++){
a[p].L=400,a[p].R=0,a[p].U=0,a[p].D=400;
for(int i=a[p-1].L;i<=a[p-1].R;i++){
for(int j=a[p-1].D;j<=a[p-1].U;j++){
if(a[p-1].vis[i][j]){
a[p].vis[i][j]=1;
int x=i-a[p-1].tx,y=j-a[p-1].ty;
a[p].vis[a[p-1].tx+y][a[p-1].ty-x]=1;
a[p].L=min({a[p].L,i,a[p-1].tx+y});
a[p].R=max({a[p].R,i,a[p-1].tx+y});
a[p].U=max({a[p].U,j,a[p-1].ty-x});
a[p].D=min({a[p].D,j,a[p-1].ty-x});
if(i==a[p-1].sx&&j==a[p-1].sy){
a[p].sx=i,a[p].sy=j;
a[p].tx=a[p-1].tx+y,a[p].ty=a[p-1].ty-x;
}
}
}
}
// cout<<a[p].L<<' '<<a[p].R<<' '<<a[p].U<<' '<<a[p].D<<'\n';
}
// for(int p=1;p<=13;p++){
// cout<<p<<":\n";
// for(int i=a[p].U;i>=a[p].D;i--){
// for(int j=a[p].L;j<=a[p].R;j++){
// cout<<(a[p].vis[j][i]?'*':' ');
// }
// cout<<'\n';
// }
// }
while(cin>>n&&n){
if(n==1){
cout<<"_|\n^\n";
continue;
}
string s[400];
int p=1;
bool flag=0;
for(int i=a[n].L,j=a[n].U;i<=a[n].R;i++){
if(a[n].vis[i][j]&&a[n].vis[i-1][j]&&a[n].vis[i+1][j]){
s[p]+='_';
flag=1;
}else{
s[p]+=' ';
}
}
if(!flag){
p=0;
s[1].clear();
}
for(int i=a[n].U-2;i>=a[n].D;i-=2){
p++;
for(int j=a[n].L;j<=a[n].R;j++){
if(a[n].vis[j][i+1]){
s[p]+='|';
}else if(a[n].vis[j][i]&&a[n].vis[j-1][i]&&a[n].vis[j+1][i]){
s[p]+='_';
}else{
s[p]+=' ';
}
}
}
for(int i=1;i<=p;i++){
while(s[i].back()==' '){
s[i].pop_back();
}
cout<<s[i]<<'\n';
}
cout<<"^\n";
}
return 0;
}
}
int main(){return asbt::main();}
UVA10723 电子人的基因 Cyborg Genes
第一问很容易想到求 LCS,用两串长度相加在减掉即可。但这样第二问不好求,直接组合数学是很不可做的,我们希望也可以 DP 求。于是对于第一问,我们设 \(f_{i,j}\) 直接表示考虑到 \(a_i\) 和 \(b_j\) 的答案。然后对于第二问,再设 \(g_{i,j}\) 表示方案数,转移过程中考察 \(f_{i,j}\) 从哪里转移过来就好了。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
int T,n,m,f[35][35],g[35][35];
string a,b;
il int F(int i,int j){
if(!i||!j){
return i+j;
}
if(~f[i][j]){
return f[i][j];
}
if(a[i]==b[j]){
return f[i][j]=F(i-1,j-1)+1;
}
return f[i][j]=min(F(i-1,j),F(i,j-1))+1;
}
il int G(int i,int j){
if(!i||!j){
return 1;
}
if(~g[i][j]){
return g[i][j];
}
if(a[i]==b[j]){
return g[i][j]=G(i-1,j-1);
}
int p=F(i-1,j),q=F(i,j-1);
if(p==q){
return g[i][j]=G(i-1,j)+G(i,j-1);
}else if(p<q){
return g[i][j]=G(i-1,j);
}else{
return g[i][j]=G(i,j-1);
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>T,cin.get();
for(int _=1;_<=T;_++){
getline(cin,a),getline(cin,b);
// cout<<a<<' '<<b<<'\n';
n=a.size(),m=b.size();
a=" "+a,b=" "+b;
memset(f,-1,sizeof(f));
memset(g,-1,sizeof(g));
cout<<"Case #"<<_<<": "<<F(n,m)<<' '<<G(n,m)<<'\n';
}
return 0;
}
}
signed main(){return asbt::main();}
LG P14339 [JOI2020 预选赛 R2] 剪刀石头布 / Rock-Scissors-Paper Expression
计数 DP,记 \(f_{0/1/2}\) 表示表达式的值为 R/S/P 的方案数,在中缀表达式求值的过程中转移即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=2e5+5,mod=1e9+7;
il int calc1(int x,int y){
if(x==0&&y==1){
return 0;
}else if(x==0&&y==2){
return 2;
}else if(x==1&&y==0){
return 0;
}else if(x==1&&y==2){
return 1;
}else if(x==2&&y==0){
return 2;
}else if(x==2&&y==1){
return 1;
}
}
il int calc2(int x,int y){
return x+y-calc1(x,y);
}
il int calc3(int x,int y){
return 3-x-y;
}
struct node{
int f[3];
node(char x=0){
f[0]=f[1]=f[2]=0;
switch(x){
case 'R':f[0]=1;break;
case 'S':f[1]=1;break;
case 'P':f[2]=1;break;
case '?':f[0]=f[1]=f[2]=1;break;
}
}
il int&operator[](int x){
return f[x];
}
il node operator+(node g)const{
node h;
for(int i:{0,1,2}){
for(int j:{0,1,2}){
if(i==j){
h[i]=(f[i]*1ll*g[j]+h[i])%mod;
}else{
int x=calc1(i,j);
h[x]=(f[i]*1ll*g[j]+h[x])%mod;
}
}
}
return h;
}
il node operator-(node g)const{
node h;
for(int i:{0,1,2}){
for(int j:{0,1,2}){
if(i==j){
h[i]=(f[i]*1ll*g[j]+h[i])%mod;
}else{
int x=calc2(i,j);
h[x]=(f[i]*1ll*g[j]+h[x])%mod;
}
}
}
return h;
}
il node operator*(node g)const{
node h;
for(int i:{0,1,2}){
for(int j:{0,1,2}){
if(i==j){
h[i]=(f[i]*1ll*g[j]+h[i])%mod;
}else{
int x=calc3(i,j);
h[x]=(f[i]*1ll*g[j]+h[x])%mod;
}
}
}
return h;
}
};
il node calc(node x,node y,char z){
return z=='+'?x+y:z=='-'?x-y:x*y;
}
int n;
char a,tc[maxn];
node tn[maxn];
string s;
stack<node> sn;
stack<char> sc;
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>s>>a;
for(int i=0;i<n;i++){
switch(s[i]){
case '(':
case '+':
case '-':
case '*':sc.push(s[i]);break;
case ')':{
int cnt=0;
while(sc.top()!='('){
tn[++cnt]=sn.top(),sn.pop();
tc[cnt]=sc.top(),sc.pop();
}
node t=sn.top();
sn.pop(),sc.pop();
for(int i=cnt;i;i--){
t=calc(t,tn[i],tc[i]);
}
if(sc.size()&&sc.top()=='*'){
t=calc(sn.top(),t,sc.top());
sn.pop(),sc.pop();
}
sn.push(t);
break;
}
default:{
node t=s[i];
if(sc.size()&&sc.top()=='*'){
t=calc(sn.top(),t,sc.top());
sn.pop(),sc.pop();
}
sn.push(t);
break;
}
}
}
int cnt=0;
while(sc.size()){
tn[++cnt]=sn.top(),sn.pop();
tc[cnt]=sc.top(),sc.pop();
}
node t=sn.top();
for(int i=cnt;i;i--){
t=calc(t,tn[i],tc[i]);
}
cout<<t[a=='R'?0:a=='S'?1:2];
return 0;
}
}
int main(){return asbt::main();}
LG P14152 千手百眼,天下人间
撤销 \(3\) 操作还需要恢复操作,非常麻烦。发现最后一个操作必然执行。进一步,可以先倒着跑一遍,处理出哪些操作需要执行,需要区间打标记,差分即可。然后再正着跑,使用线段树即可。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define lwrb lower_bound
#define uprb upper_bound
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=5e5+5,inf=1e18;
int n,m,d[maxn],ans[maxn],a[maxn],c[maxn],tr[maxn<<2],tag[maxn<<2];
struct node{
int opt,id,l,r,t,k;
}b[maxn];
il void pushup(int id){
tr[id]=max(tr[lid],tr[rid]);
}
il void pushtag(int id,int x){
tr[id]+=x,tag[id]+=x;
}
il void pushdown(int id){
if(tag[id]){
pushtag(lid,tag[id]);
pushtag(rid,tag[id]);
tag[id]=0;
}
}
il void build(int id,int l,int r){
if(l==r){
tr[id]=a[l];
return ;
}
int mid=(l+r)>>1;
build(lid,l,mid);
build(rid,mid+1,r);
pushup(id);
}
il void add(int id,int L,int R,int l,int r,int x){
if(L>=l&&R<=r){
pushtag(id,x);
return ;
}
pushdown(id);
int mid=(L+R)>>1;
if(l<=mid){
add(lid,L,mid,l,r,x);
}
if(r>mid){
add(rid,mid+1,R,l,r,x);
}
pushup(id);
}
il int query(int id,int L,int R,int l,int r){
if(L>=l&&R<=r){
return tr[id];
}
pushdown(id);
int mid=(L+R)>>1,res=-inf;
if(l<=mid){
res=max(res,query(lid,L,mid,l,r));
}
if(r>mid){
res=max(res,query(rid,mid+1,R,l,r));
}
return res;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=m;i++){
cin>>b[i].opt>>b[i].t>>b[i].l>>b[i].r;
if(b[i].opt==1){
cin>>b[i].k;
}
b[i].id=i;
}
sort(b+1,b+m+1,[](node x,node y){return x.t<y.t||x.t==y.t&&x.id<y.id;});
for(int i=1;i<=m;i++){
c[i]=b[i].t;
}
for(int i=m;i;i--){
d[i]+=d[i+1];
if(!d[i]&&b[i].opt==3){
int l=lwrb(c+1,c+m+1,b[i].l)-c,r=uprb(c+1,c+m+1,b[i].r)-c-1;
d[r]++,d[l-1]--;
}
}
build(1,1,n);
int cnt=0;
for(int i=1;i<=m;i++){
if(!d[i]&&b[i].opt<3){
if(b[i].opt==1){
add(1,1,n,b[i].l,b[i].r,b[i].k);
}else{
ans[++cnt]=query(1,1,n,b[i].l,b[i].r);
}
}
}
cout<<cnt<<'\n';
for(int i=1;i<=cnt;i++){
cout<<ans[i]<<'\n';
}
return 0;
}
}
signed main(){return asbt::main();}
LG P13994 【MX-X19-T3】「LAOI-14」Another Round
\(\operatorname{mex}\) 难以维护,直接枚举它。
假设当前枚举到了 \(p\),我们要求的就是不选 \(b_i=p\) 的 \(i\),能取到的 \(\max\{a_i\}\),在值域上做个前后缀最大值即可。于是假设有 \(c_p\) 个 \(i\) 满足 \(a_i=p\),则对于所有 \(k\in[1,n-c_p]\) 的答案有贡献 \(\max\{a_i\}-p\)。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e6+5,inf=1e18;
int T,n,a[maxn],b[maxn];
int pre[maxn],suf[maxn];
int c[maxn],ans[maxn];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>T;
while(T--){
cin>>n;
for(int i=0;i<=n;i++){
pre[i]=suf[i]=-inf;
c[i]=0,ans[i]=-inf;
}
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
b[i]=min(b[i],n);
c[b[i]]++;
pre[b[i]]=max(pre[b[i]],a[i]);
suf[b[i]]=max(suf[b[i]],a[i]);
}
for(int i=1;i<=n;i++){
pre[i]=max(pre[i],pre[i-1]);
}
for(int i=n-1;~i;i--){
suf[i]=max(suf[i],suf[i+1]);
}
// for(int i=0;i<=n;i++){
// cout<<pre[i]<<' ';
// }
// cout<<'\n';
// for(int i=0;i<=n;i++){
// cout<<suf[i]<<' ';
// }
// cout<<'\n';
for(int i=0;i<=n;i++){
ans[n-c[i]]=max(ans[n-c[i]],max(i?pre[i-1]:-inf,i<n?suf[i+1]:-inf)-i);
// cout<<i<<' '<<c[i]<<' '<<max(i?pre[i-1]:-inf,i<n?suf[i+1]:-inf)<<'\n';
}
for(int i=n-1;i;i--){
ans[i]=max(ans[i],ans[i+1]);
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<'\n';
}
}
return 0;
}
}
signed main(){return asbt::main();}
LG P14011 [POCamp 2023] 珿求 / bootfall
翻译好评👍
预处理一个 \(f_i\) 表示 \(A\ge i\) 时 \(D\) 最大是多少,DP 即可。对于询问 \((A',D')\),我们分进不进球进行讨论:
-
进球,则 \(A>D'\)。此时我们如果想赢,则要求 \(A-D'>A'-D\Leftrightarrow A+D>A'+D'\),预处理后缀 \(\max\{f_i+i\}\) 即可 \(O(1)\) 求。而如果只能做到 \(A+D=A'+D'\),那么就是平局。但如果只能做到 \(A+D<A'+D'\),并不一定就会输,因为当 \(A'<D\) 时对方是不进球的,进入下一情况即可。
-
不进球,\(A\le D'\)。此时不可能赢,如果能做到 \(D\ge A'\) 则平局,否则就只好输了。
时间复杂度 \(O(nV^2)\)。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1.6e5+5,inf=1e9;
int n,m,f[maxn],g[maxn];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
memset(f,-0x3f,sizeof(f));
f[0]=0;
for(int i=1,a,b;i<=n;i++){
cin>>a>>b;
for(int j=1.6e5;~j;j--){
f[j]=max({f[j]+b,j>=a?f[j-a]:-inf,f[j+1]});
}
}
memset(g,-0x3f,sizeof(g));
for(int i=1.6e5;~i;i--){
g[i]=max(g[i+1],f[i]+i);
}
// for(int i=0;i<=15;i++){
// cout<<f[i]<<' ';
// }
// cout<<'\n';
// for(int i=0;i<=15;i++){
// cout<<g[i]<<' ';
// }
// cout<<'\n';
int cnt1=0,cnt2=0,cnt3=0;
cin>>m;
while(m--){
int a,b;
cin>>a>>b;
if(g[b+1]>a+b){
cnt1++;
}else if(g[b+1]==a+b||f[0]>=a){
cnt2++;
}else{
cnt3++;
}
}
cout<<cnt1<<' '<<cnt2<<' '<<cnt3<<'\n';
return 0;
}
}
int main(){return asbt::main();}
LG P14207 [ROI 2016 Day1] 捕捞,还是不捕捞?
我们的路线,显然是先向上游走一段把一路上的鱼全都带上,再在向下游走的过程中卖掉。于是枚举向上游走到了哪。此时我们卖鱼的地方显然是 \(c_i\) 在值域上的一段后缀,写一个线段树二分状物即可。时间复杂度 \(O(n\log V)\)。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=5e5+5;
int n,m,kk,tr[maxn<<3],sum[maxn<<3];
struct node{
int p,b,c;
}a[maxn<<1];
il void pushup(int id){
tr[id]=tr[lid]+tr[rid];
sum[id]=sum[lid]+sum[rid];
}
il void add(int id,int l,int r,int p,int x){
if(l==r){
tr[id]+=p*x,sum[id]+=x;
return ;
}
int mid=(l+r)>>1;
if(p<=mid){
add(lid,l,mid,p,x);
}else{
add(rid,mid+1,r,p,x);
}
pushup(id);
}
il int query(int id,int l,int r,int x){
if(sum[id]<=x){
return tr[id];
}
if(l==r){
return l*x;
}
int mid=(l+r)>>1;
if(sum[rid]>=x){
return query(rid,mid+1,r,x);
}else{
return query(lid,l,mid,x-sum[rid])+tr[rid];
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m>>kk;
for(int i=1;i<=n;i++){
cin>>a[i].p>>a[i].b;
a[i].c=-1;
}
for(int i=1;i<=m;i++){
cin>>a[n+i].p>>a[n+i].b>>a[n+i].c;
}
sort(a+1,a+n+m+1,[](node x,node y){return x.p<y.p||x.p==y.p&&x.c>y.c;});
int ans=0,sum=0;
for(int i=1;i<=n+m;i++){
if(~a[i].c){
add(1,0,1e6,a[i].c,a[i].b);
}else{
sum+=a[i].b;
}
ans=max(ans,query(1,0,1e6,sum)-kk*a[i].p);
}
cout<<ans;
return 0;
}
}
signed main(){return asbt::main();}

浙公网安备 33010602011771号