T1
题意:1~n的全排列中,有k个逆序对的排列有多少种(n<=1000
思路:显然是个dp,n^3,于是前缀和优化成n^2
#include<bits/stdc++.h>
#define MAXN 1005
#define MOD 10000
using namespace std; int T,n,K;
int f[MAXN][MAXN];
int sum[MAXN][MAXN];
int main(){
freopen("permut.in","r",stdin);
freopen("permut.out","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&K);
memset(f,0,sizeof f);
memset(sum,0,sizeof sum);
f[0][0]=1;
sum[0][0]=1;
for(int i=1;i<=K;++i) sum[0][i]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<=K;++j){
f[i][j]=sum[i-1][j];
if(j-i>=0) f[i][j]-=sum[i-1][j-i];
if(f[i][j]<0) f[i][j]+=MOD;
}
sum[i][0]=1;
for(int j=0;j<=K;++j) sum[i][j]=(sum[i][j-1]+f[i][j])%MOD;
}
printf("%d\n",f[n][K]%MOD);
}
return 0;
}
T2
题意:Q次询问,f[i]表示包含a[i]且a[i]为中位数的区间的长度最长是多少(每次询问的是一段连续的f[i]中的最大值)n=2e3,q=1e5
思路:先n^2预处理一下,求出每个a[i]的f[i],求某个数是不是中位数,可以把比它小的赋值为-1,大的赋值为1,相等的就是0,然后前缀和胡搞乱搞。如果要保证这个区间包含当前这个数,可以从当前位往前往后分别枚举,使这个数前面的和加上这个数后面的正好为0。对于每个询问O(n)地查询就好(反正数据范围这么小就懒得写线段树什么的了23333)
【明明做过中位数图然而今天死磕T3去了【跪地】……不过这道题的题面也有毒2333一眼看上去感觉要在线乱搞23333瞬间觉得要GG】
【不过似乎也差不多忘了这个套路了【躺平】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MAXN 2005
using namespace std; int n,Q;
int a[MAXN];
int L[MAXN<<1],R[MAXN<<1];
int f[MAXN];
void init(){
for(int i=1;i<=n;++i){
memset(L,-1,sizeof L);
memset(R,-1,sizeof R);
L[n]=R[n]=0;
int cnt=0;
for(int j=i-1;j;--j){
if(a[j]<=a[i]) --cnt;
if(a[j]>a[i]) ++cnt;
L[n+cnt]=i-j;
}
cnt=0;
for(int j=i+1;j<=n;++j){
if(a[j]<a[i]) --cnt;
if(a[j]>=a[i]) ++cnt;
R[n+cnt]=j-i;
}
for(int j=-n;j<=n;++j)
if((~L[n+j])&&(~R[n-j]))
f[i]=max(f[i],L[n+j]+R[n-j]+1);
}
}
int read_l,read_r;
int main(){
freopen("beautiful.in","r",stdin);
freopen("beautiful.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",a+i);
init();
for(int i=1;i<=n;++i) printf("%d ",f[i]);
puts("\n");
scanf("%d",&Q);
while(Q--){
scanf("%d%d",&read_l,&read_r);
int ans=0;
for(int i=read_l;i<=read_r;++i)
ans=max(ans,f[i]);
printf("%d\n",ans);
}
return 0;
}
T3
题意:对于一个可重集合S,有三种操作:add(x) 插入x,del(x)删除x(保证存在x且每次插入删除都只影响一个),cnt(x) 询问对于x,集合中有多少个数y满足x&y==y(值域2^16)
思路:…………考过三次,今天终于会了【躺平】,O(n*√值域),对于每个数的前8位和后8位分开,用cnt[i][j]记录。
显然,O(1)插入O(n)查询是很简单的,同理,O(n)插入O(1)查询也是很简单的(稍微处理一下就好),于是对于每个数,前8位和后8位分别是修改O(1+2^8)和查询O(1+2^8)就好了,于是就优化成了O(n)的x【某c姓同学表示,2^8是常数23333
#include<bits/stdc++.h>
using namespace std; int Q;
int cnt[1<<8][1<<8];
char opt[10];
int read_x;
int main(){
freopen("subset.in","r",stdin);
freopen("subset.out","w",stdout);
scanf("%d",&Q);
while(Q--){
scanf("%s%d",opt,&read_x);
if(opt[0]=='a'){
int tmp=read_x&((1<<8)-1);
int tmp2=(read_x>>8)&((1<<8)-1);
for(register int i=0;i<(1<<8);++i){
if((tmp&i)==tmp)
++cnt[tmp2][i];
}
}
if(opt[0]=='d'){
int tmp=read_x&((1<<8)-1);
int tmp2=(read_x>>8)&((1<<8)-1);
for(register int i=0;i<(1<<8);++i){
if((tmp&i)==tmp)
--cnt[tmp2][i];
}
}
if(opt[0]=='c'){
int ans=0;
int tmp=read_x&((1<<8)-1);
int tmp2=read_x>>8;
for(register int i=0;i<(1<<8);++i){
if((tmp2&i)==i)
ans+=cnt[i][tmp];
}
printf("%d\n",ans);
}
}
return 0;
}