Public NOIP Round #2
排序
首先显然有一个\(O(n^3)\)的dp,设\(f_{i,j}\)表示栈顶是\(i\),下面一个是\(j\)的最大长度。每次枚举前一个转移。
这样子状态数就是\(O(n^2)\)的没前途,我们考虑设\(f_i\)为到了第\(i\)个做栈顶,且\(i\)出现在最终序列中的时候最大的栈长度,然后枚举前一个这样的数\(j\),显然\(a_i>a_j\),然后这两个数中间的数要求只能最多往\(a_i\)上再叠一层,且叠上去的数要小于\(a_i\)。拿着这个乱七八糟的限制我们可以直接\(O(n^2)\)dp。
发现这个形式和二维数点很像,我们考虑维护线段树,每个节点\(k\)上表示当前栈顶为\(k\),且被叠了\(0/1\)次的最大长度,转移即可。时间复杂度\(O(n\log n)\)
code:
#include<bits/stdc++.h>
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=5e5+5,M=N*4+5,K=2e3+5,mod=1000000007,Mod=mod-1;const db eps=1e-5;const int INF=1e9;
int n,m,k,x,y,z,A[N],R[N],st[N],H,dp[N],Ans;
namespace Tree{
#define ls v<<1
#define rs v<<1|1
int f[M],Fl[M],g[M];void Up(int v){f[v]=max(f[ls],f[rs]);Fl[v]=Fl[ls]|Fl[rs];g[v]=min(g[ls],g[rs]);}
void BD(int l=1,int r=n,int v=1){Me(f,-0x3f);Me(g,0x3f);}
void Ins(int x,int y,int l=1,int r=n,int v=1){if(!Fl[v]&&g[v]>y) return;if(l==r) {if(Fl[v]) Fl[v]=0,g[v]=y;else g[v]=INF,f[v]=-INF;return;}int m=l+r>>1;x<=m&&(Ins(x,y,l,m,ls),0);y>m&&(Ins(x,y,m+1,r,rs),0);Up(v);}
int Qry(int x,int y,int l=1,int r=n,int v=1){if(x<=l&&r<=y) return f[v];int m=l+r>>1;return max(x<=m?Qry(x,y,l,m,ls):-INF,y>m?Qry(x,y,m+1,r,rs):-INF);}
void Add(int x,int y,int l=1,int r=n,int v=1){if(l==r) {f[v]=y;Fl[v]=1;return;}int m=l+r>>1;x<=m?Add(x,y,l,m,ls):Add(x,y,m+1,r,rs);Up(v);}
}
int main(){
freopen("1.in","r",stdin);
int i,j;scanf("%d",&n);for(i=1;i<=n;i++) scanf("%d",&A[i]);dp[0]=0;for(i=1;i<=n;i++) {Tree::Add(A[i-1],dp[i-1]);Ans=max(Ans,dp[i]=Tree::Qry(1,A[i])+1);Tree::Ins(1,A[i]);}printf("%d\n",Ans);
}
图同构
我们考虑这个奇怪的交换方式有没有什么性质。
如果我们将每个点与其点权捆绑,可以发现这个操作方法就是每次交换使点权取反。
于是可以对每个子图分类讨论:
如果这个子图是二分图,则每个点走到目标点的路径奇偶性是固定的,则比对两个图的二元组是不是一样就好了。
如果这个子图不是二分图,则存在一个奇环,那么如果一个非环上点的颜色不对,则可以在奇数环上面绕一圈,如果一个环上的点颜色不对,则可以与相邻分别顺逆时针绕一圈,就可以调整。则答案必须要每子图都要数的种类相同,且每种颜色的奇偶性相同。
所以可以\(O(n+m)\)判即可。
code:
#include<bits/stdc++.h>
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=1e6+5,M=N*4+5,K=2e3+5,mod=1000000007,Mod=mod-1;const db eps=1e-5;const int INF=1e9;
int n,m,k,x,y,z,fa[N],W[N],Fl[N],col[N],A1[N],A2[N],B1[N],B2[N];char c;vector<int> S[N];vector<pair<int,int>> I1[N],I2[N];
int GF(int x){return fa[x]^x?fa[x]=GF(fa[x]):x;}
void GA(int x,int op,int &y){if(col[x]) {if(op^col[x])y=1;return;}col[x]=op;for(int i:S[x]) GA(i,-op,y);}
void Solve(){
int i,j;for(i=1;i<=n;i++) S[i].clear(),W[i]=fa[i]=Fl[i]=col[i]=0,I1[i].clear(),I2[i].clear();scanf("%d%d",&n,&m);for(i=1;i<=n;i++) fa[i]=i;for(i=1;i<=m;i++) scanf("%d%d",&x,&y),S[x].PB(y),S[y].PB(x),fa[GF(x)]=GF(y);
for(i=1;i<=n;i++) !col[i]&&(GA(i,1,Fl[GF(i)]),0);for(i=1;i<=n;i++) scanf("%d",&A1[i]);for(i=1;i<=n;i++) {c=Gc();while(c<'A'||c>'Z') c=Gc();B1[i]=(c=='R');}
for(i=1;i<=n;i++) scanf("%d",&A2[i]);for(i=1;i<=n;i++){c=Gc();while(c<'A'||c>'Z') c=Gc();B2[i]=(c=='R');}
for(i=1;i<=n;i++) !Fl[GF(i)]&&(I1[GF(i)].PB({A1[i],B1[i]^(~col[i]?1:0)}),I2[GF(i)].PB({A2[i],B2[i]^(~col[i]?1:0)}),0);for(i=1;i<=n;i++) Fl[GF(i)]&&(I1[GF(i)].PB({A1[i],0}),I2[GF(i)].PB({A2[i],0}),W[GF(i)]^=B1[i]^B2[i]);
for(i=1;i<=n;i++) {
if(GF(i)^i) continue;sort(I1[i].begin(),I1[i].end());sort(I2[i].begin(),I2[i].end());for(j=0;j<I1[i].size();j++) if(I1[i][j]!=I2[i][j]){puts("NO");return;}if(W[i]){puts("NO");return ;}
}puts("YES");
}
int main(){
freopen("1.in","r",stdin);
int T;scanf("%d",&T);while(T--) Solve();
}
找零
首先显然付款的时候不会付一块钱,因为付了一块钱找回来至多是一块钱,还可能更少。
那么这时候我们无论怎么付其它的钱,我们找回的一块钱数量是一定的,因此不用去管具体是什么面额,我只要知道手上有多少钱就好了。于是可以直接背包。是\(O(nX)\)的。
可以发现价值的范围是\([0,4]\),所以可以dp状态与值域互换,设\(g_i\)表示价值为\(i\)的时候至少要多少钱,然后dp是复杂度\(O(n^2)\)的。
显然可以决策单调性优化,时间复杂度\(O(n\log n)\)。不过也可以大的贪心细节dp微调也能过。
code:
#include<bits/stdc++.h>
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=5e5+5,M=N*4+5,K=2e3+5,mod=1000000007,Mod=mod-1;const db eps=1e-5;const int INF=1e9;
int n,m,k,z,Ans,ToT,H;ll f[N],g[N],x,y,A[N];vector<ll> S[5];
void Solve(int l,int r,int x,int y,int op){//cerr<<l<<' '<<r<<' '<<x<<' '<<y<<' '<<op<<endl;
if(l>r||x>y) return;int m=l+(r-l)/op/2*op,p=y,i;for(i=x;i<=min(y,m);i+=op) (m-i)/op<=H&&g[i]+A[(m-i)/op]<f[m]&&(f[m]=g[i]+A[(m-i)/op],p=i);Solve(l,m-op,x,p,op);Solve(m+op,r,p,y,op);
}
int calc(int x,int y,int z){while(x%y!=z) x--;return x;}
int main(){
freopen("1.in","r",stdin);
int i,j;scanf("%d%lld",&n,&x);Ans=x%5;x=x/5*5;Me(f,0x3f);f[0]=0;for(i=1;i<=n;i++)scanf("%lld",&y),z=(y+4)/5*5-y,y+=z,S[z].PB(y);
for(i=1;i<=4;i++) {
Mc(g,f);Me(f,0x3f);H=0;for(j=0;j<S[i].size();j++) A[++H]=S[i][j];sort(A+1,A+H+1);for(j=1;j<=H;j++) A[j]+=A[j-1];
for(j=0;j<i;j++)Solve(j,calc(4*n,i,j),j,calc(4*n,i,j),i);}
for(i=4*n;~i;i--) if(f[i]<=x){printf("%d\n",i+Ans);return 0;}
}