设f[i]表示选择了第i行时能保留的最大行数
那么显然 f[i]=max(1,f[j]+1) , { $ j < i,第j行中每一列的数均大于第i行 $ }
直接按上式dp即可
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int ha=1e9+7;
const int N=1e6+5;
ll A[505][505],f[505];
ll n,m;
bool isbig(int i1,int i2){
for(int i=1;i<=m;++i)
if(A[i1][i]<=A[i2][i])return 0;
return 1;
}
int main(){
std::ios_base::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=m;++i)A[0][i]=-(1LL<<60);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
cin>>A[i][j];
}
ll ans=1;
f[1]=1;
for(int i=2;i<=n;++i){
f[i]=1;
for(int j=1;j<i;++j)
if(isbig(j,i))f[i]=max(f[i],f[j]+1);
ans=max(ans,f[i]);
//cout<<f[i]<<endl;
}
cout<<n-ans;
return 0;
}
我们从根开始做dfs, 并用一个树状数组$B$记录目前扫描过的所有点的权值的个数
每次走到一个点$i$, 我们先记录下$B.query(V_i-K,V_i+K)$的值
然后扫描这个点的所有子树, 扫描完毕后, 再次记录$B.query(V_i-K,V_i+K)$的值并减去之前的值
最后的值就是当前点的子树对当前点的贡献, 直接将其记录即可
最后将每个点的贡献全部加起来就是答案
特别地, 数值的范围是$10^9$, 所以我们需要先用一个map记录所有会用到的数并将其按顺序离散化处理
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int ha=1e9+7;
const int N=3e5+5;
ll B[N*2];
void U(int i,ll x){++i;while(i<N*2)B[i]+=x,i+=i&-i;}
ll Q(int i){++i;ll r=0;while(i)r+=B[i],i-=i&-i;return r;}
ll A[N],L[N],R[N],fa[N],ans[N],n,K,a,l,r;
vector<int>G[N];
vector<int>WW;
map<int,int>Z;
void readll(ll &x){
char s=getchar();
while(~s&&s<'0'||s>'9')s=getchar(); x=s-'0';
while(~(s=getchar())&&s>='0'&&s<='9')x=x*10+s-'0';
}
ll dfs(int u){
ll res=0;
res-=Q(R[u])-Q(L[u]); //old sum
U(A[u],1);
for(auto &v:G[u]){
dfs(v);
res+=ans[v];
}
res+=Q(R[u])-Q(L[u]); //now sum
return ans[u]=res;
}
int main(){
readll(n);
readll(K);
for(int i=1;i<=n;++i){
readll(A[i]);
L[i]=A[i]-K;
R[i]=A[i]+K;
Z[L[i]]=Z[A[i]]=Z[R[i]]=1;
}
int cnt=0;
for(auto &i:Z)i.second=++cnt;
for(int i=1;i<=n;++i){
L[i]=Z[L[i]];
A[i]=Z[A[i]];
R[i]=Z[R[i]];
}
for(int i=2;i<=n;++i){
readll(fa[i]);
G[fa[i]].push_back(i);
}
dfs(1);
for(int i=1;i<=n;++i)
printf("%lld\n",ans[i]);
return 0;
}
没啥好说的, 直接读取每个独立的字符串并特判其是否等于"henan", 是就替换即可
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int ha=1e9+7;
const int N=1e6+5;
ll A[505][505];
ll n,m;
vector<int>V;
char s[1000];
int main(){
char c;
while(1){
while((c=getchar())==' ')putchar(' ');
if(~c&&c!='\r'&&c!='\n'){
s[0]=c;
scanf("%s",s+1);
if(string("henan")==s)printf("Henan");
else printf(s);
}else break;
}
return 0;
}
直接找出出现次数最多的那个数即可
#include<bits/stdc++.h>
using namespace std;
int A[1025];
int main()
{
int n,x;
cin>>n;
while(n--){
cin>>x;
A[x]++;
}
x=0;
for(int i=1;i<=100;++i)
if(A[x]<A[i])x=i;
printf("%d\n",x);
}
首先分析性质, 假设$a=7$, 那么$7/1=7, 14/2=7, 15/2=7.5, 21/3=7, 22/3=7.33, 23/3=7.66 $ ...
也就是说这是一个等差数列, 直到 $49/7=7$ 为止, 也就是$a^2$, 不难发现大于 $ a^2 $的数一定有解
所以我们把大于的部分特判掉直接计入答案, 然后加上一个等差数列再减去多加的部分即可
另外假设$F(a,l,r)$为答案的话, 显然$F(a,l,r) = F(a,0,r)-F(a,0,l-1)$, 这样可以忽略左端点, 少做一些特判
还有就是小心溢出(虽然我直接用了__int128过了就是了
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int ha=1e9+7;
const int N=1e6+5;
ll n,a,l,r;
void readll(ll &x){
char s=getchar();
while(~s&&s<'0'||s>'9')s=getchar(); x=s-'0';
while(~(s=getchar())&&s>='0'&&s<='9')x=x*10+s-'0';
}
ll solve(ll a,ll r){
if(a>r)return 0;
ll ans=0;
if((__int128)a*a<=r){
ans+=r-a*a+1;
r=a*a;
}
if((__int128)a*a>r){
ll z=r/a;
ans+=z*(z+1)/2;
ll t=z*a+z-1;
if(t>r)ans-=t-r;
} else ans+=a*(a-1)/2;
return ans;
}
int main(){
//freopen("a.in","r",stdin);
readll(n);
while(n--){
readll(a);
readll(l);
readll(r);
ll ans=solve(a,r)-solve(a,l-1);
printf("%lld\n",ans);
}
return 0;
}
从左向右扫描序列, 先忽略当前点 $A_i$ 对应的子树, 然后看下个点 $A_{i+1}$ 是否已经被忽略过
若没有, 直接暴力找 $LCA(A_i,A_{i+1})$ 在 $A_i$ 那侧的儿子 $P$, 然后忽略 $P$ 的整棵子树即可
而如果$A_{i+1}$之前被忽略过, 答案一定是不合法的
正确性证明:
- 由题意可得, 假设要写出一个点x, 那么x的所有后代一定已经被写出过或被忽略了(儿子永远比父亲先写出)
- 如果上次访问的点是x, 那么在不考虑忽略额外的点的情况下, y一定是x的父亲对应子树上的叶子节点(由题意显然可得)
- 考虑忽略父亲以外的点, 那么y可以是x的父亲对应子树上的任何节点, 只要忽略掉y的子树即可(当然, 除了y自身)
- 考虑忽略父亲, 那么此时直接忽略掉父亲的子树上所有未被写出过的点, y将可以是x的"父亲的父亲"对应子树上的任何节点(从2,3可以得到)
- 由4推广得到, 只要x向上不断忽略父亲对应的子树, y一定可以被合法写出, 除非y已经被忽略
复杂度证明
首先证明忽略子树的操作是均摊O(1)的
不难发现我们每次忽略某个点都一定会忽略整棵子树
所以我们遇到被忽略的点就可以直接跳过并忽略下一棵子树了
显然每个点最多只会做一次忽略, 所以复杂度是O(n+m), 因为是树所以总体复杂度是O(n)
然后证明LCA部分的复杂度是均摊O(1)的
暴力求LCA的复杂度其实就是求LCA时每个点会被"经过"次数的总和
求过LCA后, 上个点那侧的子树会被全部忽略, 所以显然经过次数不会再增加了(仅本次+1)
而另一侧的那个点, 在下一次求LCA后仍然是本侧的子树被忽略
但只有在LCA以下的那些点才会经过次数+1(且被忽略), LCA的祖先不会被忽略也不会被经过
到此时不难发现, 每个点最多只会被经过两次, 一次是作为"目标点"那边找LCA(不被忽略), 另一次是作为"出发点"那边找LCA(被忽略)
特别地, LCA本身可能会被"经过"多次, 但由于是单点, 且每次求LCA只会有一个LCA, 所以均摊后的复杂度仍然是O(1)
理论上, 倍增或ST之类的东西来预处理求LCA也是可行的, 但是由于巨大的常数和空间开销反而容易出现MLE和TLE
总结: 暴力出奇迹(划)
什么那LCA怎么求? 把深度暴力对齐然后不断暴力找父亲就好了
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int ha=1e9+7;
const int N=1e6+5;
void readll(ll &x){
char s=getchar();
while(~s&&s<'0'||s>'9')s=getchar(); x=s-'0';
while(~(s=getchar())&&s>='0'&&s<='9')x=x*10+s-'0';
}
void readint(int &x){
char s=getchar();
while(~s&&s<'0'||s>'9')s=getchar(); x=s-'0';
while(~(s=getchar())&&s>='0'&&s<='9')x=x*10+s-'0';
}
struct node{node *t;int v;}E[N];
node *G[N];
int D[N],V[N],B[N],fa[N];
void dfs(int u,int d){
D[u]=d;
for(node *vv=G[u];vv;vv=vv->t)
dfs(vv->v,d+1);
}
void rush(int u){
for(node *vv=G[u];vv;vv=vv->t)
if(!V[vv->v])rush(vv->v);
V[u]=1;
}
int get(int x,int y){
while(D[x]<D[y])y=fa[y];
while(D[x]>D[y])x=fa[x];
while(fa[x]!=fa[y])x=fa[x],y=fa[y];
return x;
}
int main(){
ll T,n,m,ttt;
readll(T);
while(T--){
readll(n); readll(m);
for(int i=1;i<=n;++i)G[i]=0,V[i]=0;
for(int i=2;i<=n;++i){
readint(fa[i]);
E[i].t=G[fa[i]]; E[i].v=i;
G[fa[i]]=E+i;
}
dfs(1,0);
int ans=1;
for(int i=1;i<=m;++i)readint(B[i]);
for(int i=1;i<=m;++i){
if(!V[B[i]]){
rush(B[i]);
if(i>1){
int x=get(B[i-1],B[i]);
rush(x);
}
} else {
ans=0;
break;
}
}
puts(ans?"NOT BAD":"BAD GUGU");
}
}
暴力枚举每一个状态然后用猜测串不断删掉不符合条件的状态即可, 想办法压压常剪剪枝就过了
sbzdw把输出的前导零吃了结果场上并没有过, 太菜了
#include <bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;
const int ha=1e9+7;
const int N=1e6+5;
void readll(ll &x){
char s=getchar();
while(~s&&s<'0'||s>'9')s=getchar(); x=s-'0';
while(~(s=getchar())&&s>='0'&&s<='9')x=x*10+s-'0';
}
void readint(int &x){
char s=getchar();
while(~s&&s<'0'||s>'9')s=getchar(); x=s-'0';
while(~(s=getchar())&&s>='0'&&s<='9')x=x*10+s-'0';
}
struct node{
int v;
node *nxt;
node(){}
}*S,g[10000000]; int tot;
//int f[1000][1000],tot;
int now,n,m;
int V[10];
/*
int culc(int x,int y){
int cnt=0;
if(x%10==y%10)++cnt; x/=10; y/=10;
if(x%10==y%10)++cnt; x/=10; y/=10;
if(x%10==y%10)++cnt;
return cnt;
}
*/
void dfs(int i){
if(i==m){
//assert(now>=0);
// printf("%d\n",now);
g[tot].nxt=S;
g[tot].v=now;
S=g+tot++;
return;
}
for(int x=0;x<=9;++x){
if(!V[x]){
V[x]=1;
now=now*10+x;
dfs(i+1);
now/=10;
V[x]=0;
}
}
}
void init(){/*
for(int i=0;i<1000;++i){
for(int j=i;j<1000;++j)
f[i][j]=f[j][i]=culc(i,j);
}*/
readint(n);readint(m); S=0; now=0;
dfs(0);
g[tot].nxt=S;
S=g+tot++;
}
int judge(int x,int y){
int cnt1=0,cnt2=0;
int C1[10],C2[10];
for(int i=0;i<=9;++i)C1[i]=C2[i]=0;
for(int i=1;i<=m;++i){
if(x%10==y%10)++cnt1;
else ++C1[x%10],++C2[y%10];
x/=10; y/=10;
}
for(int i=0;i<=9;++i)cnt2+=min(C1[i],C2[i]);
return cnt1*100+cnt2;
}
int32_t main(){
init();
//int ww=0;
for(int i=1,md,y,x;i<=n;++i){
readint(md);readint(x);readint(y);
node *fa=S;
while(fa->nxt){
node *now=fa->nxt;
if(judge(now->v,md)!=x*100+y){
fa->nxt=now->nxt;//++ww;
continue;
}
fa=fa->nxt;
}
}
//cout<<ww<<" "<<tot<<endl;
printf(((string)"%0"+(char)('0'+m)+"d\n").c_str(),S->nxt->v);
}