T1(exgcd/ksm求乘法逆元)
思路:
看到
,长得就像
,于是联想到乘法逆元
一看数据范围:
对于所有测试点,保证 , ,P为质数
暴力枚举肯定是不现实,于是考虑只计算前两项,用前两项推出第三项
代码:
分情况讨论:
将当前的
数组手动离散化存在
这个结构体内,在离散化处理的同时计算出逆元
然后利用简单组合数学(手玩几组数据)求出当前状态的所有方案数,累加即可
#include <bits/stdc++.h>
using namespace std;
const int N=3000;
int P;
int n,m;
int a[N];
struct node{
int val,inv;
int cnt[4]={0,0,0,0};
}nums[N];
int find(int key){
int l=-1,r=m;
int mid;
while(l+1<r){
mid=(l+r)>>1;
if(nums[mid].val<key){
l=mid;
}
else r=mid;
}
if(r<m&&nums[r].val==key) return r;
return -1;
}
int qpow(int a,int b){
int ans=1%P;
for(;b;b>>=1){
if(b&1)ans=(long long)ans*a%P;
a=(long long)a*a%P;
}
return ans;
}
bool cmp_val(const node &a,const node &b){
return a.val<b.val;
}
inline int read(){
int cnt=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
return cnt*f;
}
int main(){
n=read(),P=read();
for(int i=0;i<n;i++){a[i]=read();}sort(a,a+n);
int stamp=0,tmp_cnt=1;
for(int i=1;i<=n;i++){
if(i==n||(i>0&&a[i]!=a[i-1])){
int x=a[i-1]%P,j=0;
if(x>0){
while(j<stamp&&nums[j].val!=x){++j;}
if(j==stamp){stamp++;nums[j].val=x;nums[j].inv=qpow(x,P-2);}
++nums[j].cnt[min(3,tmp_cnt)];
}
tmp_cnt=1;
}
else ++tmp_cnt;
}
m=stamp;
for(int i=0;i<m;i++){
nums[i].cnt[2]+=nums[i].cnt[3];
nums[i].cnt[1]+=nums[i].cnt[2];
}
sort(nums,nums+m,cmp_val);
int ans=0;
for(int i=0;i<m;i++){
for(int j=i;j<m;j++){
int z=1LL*nums[i].inv*nums[j].inv%P;
if(i!=j&&(z<=nums[i].val||z<=nums[j].val)) continue;
int k=find(z);
if(k==-1) continue;
int tmp=0;
if(nums[i].val!=nums[j].val){
tmp+=nums[i].cnt[1]*nums[j].cnt[1]*nums[k].cnt[1];
}
else{
if(nums[i].val!=nums[k].val){
tmp+=nums[i].cnt[1]*(nums[i].cnt[1]-1)/2*nums[k].cnt[1];
tmp+=nums[i].cnt[2]*nums[k].cnt[1];
}
else{
tmp+=nums[i].cnt[3];
tmp+=nums[i].cnt[2]*(nums[i].cnt[1]-1);
tmp+=1LL*nums[i].cnt[1]*(nums[i].cnt[1]-1)*(nums[i].cnt[1]-2)/6;
}
}
ans+=tmp;
}
}
printf("%d",ans);
return 0;
}
T2(有限制的二维偏序问题)
此处的树状数组可以理解为权值线段树,考虑最短链长度的选取即可
内网题解
本题与传统的二维偏序问题类似,只需要仔细考虑维度、顺序问题。
对于一条长度为 的链,贪心地,我们发现用容量最大的 个背包一定是最优的背包安排。
因此将所有点按 降序排序,并依次枚举所有点,用权值树状数组维护每个 坐标结尾的最长链长度,注意需要维护能够存的下当前点背包数量,并在更新树状数组时将更新的答案与这个值取 min。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+50;
int T,b[N],t[N],n,m,siz=0;
struct BIT{
int c[N];
int lowbit(int x){return x&(-x);}
void clear(){memset(c,0,sizeof(c));}
void add(int x,int v){for(;x;x-=lowbit(x)) c[x]=max(c[x],v);}
int query(int x){int ans=0;for(;x<=siz;x+=lowbit(x)) ans=max(ans,c[x]);return ans;}
}bit;
inline int read(){
int cnt=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
return cnt*f;
}
struct node{int w,v;}a[N];
bool cmp(const node &a,const node &b){if(a.w!=b.w)return a.w<b.w;return a.v<b.v;}
void solve(){
bit.clear(),siz=0;
n=read();
for(int i=1;i<=n;i++){a[i].w=read(),a[i].v=read();b[++siz]=a[i].v;}
sort(a+1,a+1+n,cmp);reverse(a+1,a+1+n);
sort(b+1,b+1+siz);siz=unique(b+1,b+1+siz)-(b+1);
m=read();
for(int i=1;i<=m;i++){t[i]=read();}sort(t+1,t+1+m);reverse(t+1,t+1+m);
int ans=0,p=0;
for(int i=1;i<=n;i++){
while(p+1<=m&&t[p+1]>=a[i].w)++p;
int x=lower_bound(b+1,b+siz+1,a[i].v)-b;
int f=min(bit.query(x)+1,p);
bit.add(x,f),ans=max(ans,f);
}
printf("%d\n",ans);
}
int main(){
T=read();
while(T--) solve();
return 0;
}
T3(动态规划)
状态转移方程得出:
即:原子树内部的组合方式
分出去次大编号子树的组合方式
剩余子树的组合方式
代码不是我的,我只是个蒟蒻罢了
#include <bits/stdc++.h>
using namespace std;
const int P=998244353;
const int N=500+10;
int c[N][N];
int n;
int dp[N][N];
bool ban[N];
int main(){
int k;
scanf("%d%d\n",&n,&k);
memset(c,0,sizeof(c));
for (int i=0;i<=n;++i){
c[i][0]=1;
for (int j=1;j<=i;++j)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%P;
}
memset(ban,0,sizeof(ban));
for (int i=1;i<=k;++i){
int x;
scanf("%d",&x);
ban[x]=1;
}
memset(dp,0,sizeof(dp));
dp[1][1]=ban[1]?0:1;
for (int d=2;d<=n;++d){
for (int i=1;i<=n;++i){
if (i==1){
dp[i][d]=1;
continue;
}
for (int j=1;j<i;++j){
dp[i][d]=(dp[i][d]+1LL*dp[i-j][d]*dp[j][d-1]%P*c[i-2][j-1])%P;
}
}
for (int i=1;i<=n;++i)
if (ban[i])
dp[i][d]=0;
}
int L,R;
scanf("%d%d",&L,&R);
for (int i=L;i<=R;++i){
printf("%d",(dp[n][i]-dp[n][i-1]+P)%P);
if (i!=R) putchar(' ');
}
puts("");
return 0;
}