231025校内赛
T1 矩阵
一道容斥题我容斥一直都不是很好
对于所有的最大值限制来说可以按照 \(max\) 值来排序,每次处理所有相同的值
算出来之后可以直接删除当前行或列,因为剩余的都是更大的 \(max\) ,当前行或列自然满足后面的限制
假设有 \(r\) 行 \(c\) 列的限制最大值等于 \(max\) ,那么这些位置不能超过 \(max\) 且每一行或列都有至少一个值取到 \(max\)
设 \(i,j\) 分别为有 \(i\) 行 \(j\) 列没有取到最大值,\(t\) 表示 \(i\) 行 \(j\) 列覆盖的位置数,\(tot\) 表示 \(r\) 行 \(c\) 列总位置数,那么贡献就是 \((-1)^{i+j} \binom r i \binom c j (max-1)^t max^{tot-t}\)
注意:\(n,m\) 每次计算完之后都要减去 \(r,c\)
#include<bits/stdc++.h>
#define N 4010
#define ll long long
#define mod ((int)1e9+9)
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
int n,m,k,C[N][N];
pii p[N];
int ksm(int x,int y){
int res = 1;
while(y){
if(y&1) res = 1ll*res*x%mod;
x = 1ll*x*x%mod;
y>>=1;
}
return res;
}
int main(){
freopen("mat.in","r",stdin);
freopen("mat.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i = 1;i<=n;i++){
k++;
cin>>p[k].fi;
p[k].se = 0;
}
cin>>m;
for(int i = 1;i<=m;i++){
k++;
cin>>p[k].fi;
p[k].se = 1;
}
sort(p+1,p+k+1);
for(int i = 0;i<=k;i++){
C[i][0] = 1;
for(int j = 1;j<=i;j++)
C[i][j] = (C[i-1][j-1]+C[i-1][j])%mod;
}
int ans = 1,res;
for(int l = 1,r = 0;l<=k;l = r+1){
res = 0;
int cnt[2] = {0,0},mx = p[l].fi,lim[2];
while(r+1<=k&&p[r+1].fi==p[l].fi)
r++,cnt[p[r].se]++;
lim[0] = cnt[0];lim[1] = cnt[1];
if(!mx) lim[0] = lim[1] = 0;
int v = ans,tot = (cnt[0]*m%mod+cnt[1]*n%mod-cnt[0]*cnt[1]%mod+mod)%mod;
for(int pos = 0;pos<=lim[0];pos++){
for(int c = 0;c<=lim[1];c++){
int tmp = 1ll*C[cnt[0]][pos]*C[cnt[1]][c]%mod;
int t = (pos*m%mod+c*n%mod-pos*c%mod+mod)%mod;
tmp = 1ll*tmp*ksm(mx,t)%mod;
tmp = 1ll*tmp*ksm(mx+1,tot-t)%mod;
tmp = 1ll*tmp*v%mod;
if((pos+c)&1) res = (res-tmp+mod)%mod;
else res = (res+tmp)%mod;
}
}
for(int i = l;i<=r;i++)
if(p[i].se==0) n--;
else m--;
ans = res;
}
cout<<ans;
return 0;
}
T2 图论
考虑修改操作对之后每次询问的影响,当询问 \(u\) 的点权时,往前找到最后一次能影响到 \(u\) 的赋值操作
再将这个赋值操作之后的所有能影响到 \(u\) 的取 \(min\) 操作拿出来,对修改权值一起求个最小值就是答案。
于是可以先用 \(bitset\) 预处理传递闭包,将所有的修改操作存在栈里,询问时在栈中找出所有有影响的操作,计算出点权
当栈的大小达到 \(O(\sqrt{n})\) 时,将栈中所有操作依次暴力执行并将栈清空即可,时间复杂度 \(O(\frac{n^2}{w} + n\sqrt{n})\)
如果最后两个测试点空间不够,可以考虑拿时间换空间
第一次选出 \(\frac{n}{k}\) 个点,只回答它们的询问,那么预处理传递闭包时也只需要记录每个点能否到达这 \(\frac{n}{k}\) 个点,这样做 \(k\) 次,每次将 \(bitset\) 的空间拿来重复利用
时间复杂度乘了 \(k\) ,空间复杂度除以了 \(k\)
#include<bits/stdc++.h>
#define N 50010
#define B 450
using namespace std;
struct edge{
int v,ne;
}e[N];
int n,m,q,cnt,tim,hd,tl,h[N],opt[N],x[N],y[N],bl[N];
int f[130][N],tmp[N],val[N],ans[N],idx[N],vis[N];
bitset<(N>>1)>g[N];
bool cmp(int i,int j){
return y[i]<y[j];
}
void add(int u,int v){
e[++cnt].v = v;
e[cnt].ne = h[u];
h[u] = cnt;
}
void cover(int u,int v,int *dp,int t){
if(vis[u]==tim) return ;
vis[u] = tim;dp[u] = v;idx[u] = t;
for(int i = h[u];i;i = e[i].ne)
cover(e[i].v,v,dp,t);
}
void getset(int u){
if(vis[u]==tim) return ;
vis[u] = tim;
g[u].reset();
if(u>=hd&&u<=tl) g[u][u-hd] = 1;
for(int i = h[u];i;i = e[i].ne)
getset(e[i].v),g[u]|=g[e[i].v];
}
int main(){
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m>>q;
for(int i = 1;i<=m;i++){
int u,v;
cin>>u>>v;
add(u,v);
}
for(int i = 1;i<=q;i++){
cin>>opt[i]>>x[i];
if(opt[i]<=2) cin>>y[i];
bl[i] = (i-1)/B+1;
}
for(int i = 1;i<=q;i++){
if(bl[i]!=bl[i+1]){
memset(f[bl[i]],0x3f,sizeof(f[bl[i]]));
int len = 0;
for(int j = i;bl[j]==bl[i];j--)
if(opt[j]==2) tmp[++len] = j;
sort(tmp+1,tmp+len+1,cmp);
tim++;
for(int j = 1;j<=len;j++)
cover(x[tmp[j]],y[tmp[j]],f[bl[i]],0);
}
}
hd = 1;tl = min(n,(N>>1));
while(hd<=n){
memset(val,0,sizeof(val));
memset(idx,0,sizeof(idx));
tim++;
for(int i = 1;i<=n;i++)
getset(i);
for(int i = 1,len = 0;i<=q;i++){
if(opt[i]==1){
tmp[++len] = i;
if(len==B){
tim++;
while(len){
cover(x[tmp[len]],y[tmp[len]],val,tmp[len]);
len--;
}
}
}else if(opt[i]==3&&x[i]>=hd&&x[i]<=tl){
int res = val[x[i]],l = idx[x[i]]+1,r = i-1;
for(int j = len;j;j--)
if(g[x[tmp[j]]][x[i]-hd]){
res = y[tmp[j]];
l = tmp[j]+1;
break;
}
if(l>r);
else if(bl[l]==bl[r]){
for(int j = l;j<=r;j++)
if(opt[j]==2&&g[x[j]][x[i]-hd])
res = min(res,y[j]);
}else{
while(bl[l]==bl[l-1]){
if(opt[l]==2&&g[x[l]][x[i]-hd]) res = min(res,y[l]);
l++;
}
while(bl[r]==bl[r+1]){
if(opt[r]==2&&g[x[r]][x[i]-hd]) res = min(res,y[r]);
r--;
}
for(int j = bl[l];j<=bl[r];j++)
res = min(res,f[j][x[i]]);
}
ans[i] = res;
}
}
hd = tl+1;
tl = min(n,hd+(N>>1)-1);
}
for(int i = 1;i<=q;i++)
if(opt[i]==3) cout<<ans[i]<<"\n";
return 0;
}
T3 排序
对于每一次的冒泡排序我们考虑它究竟会执行多少次
每个数前面最大数如果比它大那么就会被放到后面
所以一个数的答案就是它前面有多少个数大于它
设一个数的答案为 \(f_i\) ,所有的数的答案 \(\max{f_i}\)
考虑 \(f_i = i-g_i\) 那么 \(g_i\) 表示前面的 \(\le a_i\) 的数
但是有一个性质,如果有 \(i < j,a_i\ge a_j\) 那么 $f_i < f_j $
那么 \(g_i\) 就可以改为所有数中 \(\le a_i\) 的数,这样只会影响本就不会作为答案的 \(f_i\)
可以用权值线段树来实现
#include<bits/stdc++.h>
#define N 1000010
#define pii pair<int,int>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
struct node{
int l,r,mx,flag;
}t[N<<2];
int n,m,q,a[N],x[N],y[N],lp[N],pos;
pii b[N],c[N];
void pushnow(int p,int v){
t[p].mx+=v;t[p].flag+=v;
}
void pushdown(int p){
if(t[p].flag){
pushnow(lc,t[p].flag);
pushnow(rc,t[p].flag);
t[p].flag = 0;
}
}
void build(int p,int l,int r){
t[p].l = l;t[p].r = r;
if(l==r) return ;
int mid = (l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
}
void add(int p,int l,int r,int v){
if(t[p].l>=l&&t[p].r<=r){
pushnow(p,v);
return ;
}
pushdown(p);
int mid = (t[p].l+t[p].r)>>1;
if(l<=mid) add(lc,l,r,v);
if(r>mid) add(rc,l,r,v);
t[p].mx = max(t[lc].mx,t[rc].mx);
}
int main(){
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>q;
for(int i = 1;i<=n;i++){
cin>>a[i];
c[i] = b[i] = {a[i],i};
}
for(int i = 1;i<=q;i++){
cin>>x[i]>>y[i];
b[i+n] = {y[i],i+n};
}
m = n+q;
sort(b+1,b+m+1);
for(int i = 1;i<=m;i++)
if(b[i].fi==b[i-1].fi)
lp[i] = lp[i-1];
else lp[i] = i;
build(1,1,m);
for(int i = 1;i<=n;i++){
pos = lower_bound(b+1,b+m+1,c[i])-b;
add(1,pos,pos,i);
add(1,lp[pos],m,-1);
}
for(int i = 1;i<=q;i++){
pos = lower_bound(b+1,b+m+1,c[x[i]])-b;
add(1,pos,pos,-x[i]);
add(1,lp[pos],m,1);
c[x[i]] = {y[i],i+q};
pos = lower_bound(b+1,b+m+1,c[x[i]])-b;
add(1,pos,pos,x[i]);
add(1,lp[pos],m,-1);
cout<<t[1].mx<<"\n";
}
return 0;
}
梦与现实间挣扎着,所求为何
你可以借走我的文章,但你借不走我的智慧 虽然我是傻逼本文来自博客园,作者:cztq,转载请注明原文链接:https://www.cnblogs.com/cztq/p/17796304.html