test43
法阵
喜欢 cf3100 的 t1 吗,感觉这个题目推推推是笨笨的,应该直接打表观察才对!
先把题意变成大脑更容易接受的样子:横非空,纵非满,横灯连,纵空连。
我们考虑“横非空,横灯连”的形态,双射 \(n\) 个 \(1\leq l_i\leq r_i\leq m\)。
再看一下“纵非满,纵空连”带来的约束,纵着看不能有 \(1\) 前后夹着 \(0\) 但是反之可以(无歧义),所以 \(\forall i<j<k\) 有 \([l_j,r_j]\subseteq [l_i,r_i]\cup[l_k,r_k]\),这个是充要的。
进一步的,不合法当且仅当存在 \(v\in [l_i,r_i]\),纵向 “ 存在前缀 \([l_j,r_j](j<i)\) 没有 \(v\) ” 且 " 存在后缀 \([l_j,r_j](j>i)\) 没有 \(v\) ",所以合法必须要前缀或者后缀上都有 \(v\)。
试一试发现最好的切入点是“纵空连、纵非满”一定会将纵向割成前后缀,划开了上一步的 or 关系。
现在只考虑上半部分即前缀,下一行总是上一行的子区间,所以划分的那条线,只要满足先降后升即可。
现在考虑对于大前缀的那条线能拼什么样的大后缀线,首先对称的后缀线要满足先升后降。
设前缀线的底端是 \(a_{i,l\to r}\),后缀线的顶端是 \(a_{j,p\to q}\),自己手摸一下有两大情况(
-
\(i\geq j\),此时 \(r+1=p\) 或 \(q+1=l\),因为水平对称只考虑前者。写着写着可以发现旋转一个直角变成下一种情况,只要额外要求 \(r+1\neq p\) 即可,改一下前缀和那里就好了。
-
\(i<j\),此时 \(i+1=j\),\([l,r]\) 和 \([p,q]\) 不交,因为水平对称只考虑 \(r<p\)。
考虑枚举 \(r,p\),之后是四条路径的走法方案数量相乘,对 \(r\) 的两条路径乘积做前缀和即可。
具体考虑方案数的时候可以看格子的边角,然后考虑哪些轮廓是固定的,然后不定轮廓的计数就是走法数。
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)using namespace std;const int N=5005, P=998244353;int n, m, mul[N], inv[N], sum[N], ans;inline int C(int n,int m) {if(m<0||n<m) return 0;return mul[n]*inv[m]%P*inv[n-m]%P;
}int coel(int x,int y) { return C(x+y,x); }signed main() {freopen("magic.in","r",stdin);freopen("magic.out","w",stdout);ios::sync_with_stdio(0);cin.tie(0);mul[0]=inv[0]=inv[1]=1;up(i,1,5e3) mul[i]=mul[i-1]*i%P;up(i,2,5e3) inv[i]=inv[P%i]*(P-P/i)%P;up(i,2,5e3) inv[i]=inv[i-1]*inv[i]%P;cin >> n >> m;up(i,1,n-1) {up(j,1,m) {int val=coel(i,j-1)*coel(i-1,m-j)%P;sum[j]=(sum[j-1]+val)%P;}up(j,1,m-1) {int val=sum[j]*coel(n-i-1,j)%P*coel(n-i,m-j-1)%P;ans=(ans+val)%P;}}swap(n,m);up(i,1,n-1) {up(j,1,m) {int val=coel(i,j-1)*coel(i-1,m-j)%P;sum[j]=(sum[j-1]+val)%P;}up(j,1,m-1) {int val=sum[j-1]*coel(n-i-1,j)%P*coel(n-i,m-j-1)%P;ans=(ans+val)%P;}}cout << 2*ans%P << '\n';return 0;
}
连通块
时光倒流变成加边,现在你要想想怎么找连通块里面离 \(u\) 最远的,诶离 \(u\) 最远的肯定可以作为直径的一段,进一步的 \(u\) 走到离自己最远的点肯定要经过直径中点,再进一步的考虑任意一组直径,考虑 \(u\) 到两个断电的更大值肯定就是对的了。用 \((x,y)\) 合并两个集合是简单的,考虑跨国集合的贡献,一定走到 \(x,y\) 的,那就分别考虑子集里面里 \(x/y\) 最远的,显然可以就拿你那原本的端点来更新。
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pb push_backusing namespace std;const int N=200005, M=21;int n, m, T, u[N], v[N], dep[N], fa[N][M], l[N], r[N], dsu[N];
int opt[N], id[N], ans[N], del[N];
vector<int> to[N];void dfs(int x,int fad) {dep[x]=dep[fad]+1, fa[x][0]=fad;up(i,1,T) fa[x][i]=fa[fa[x][i-1]][i-1];for(int y:to[x]) if(y!=fad) dfs(y,x);
}int lca(int x,int y) {if(dep[x]<dep[y]) swap(x,y);dn(i,T,0) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];if(x==y) return x;dn(i,T,0) if(fa[x][i]!=fa[y][i]) x=fa[x][i], y=fa[y][i];return fa[x][0];
}int dis(int x,int y) {return dep[x]+dep[y]-2*dep[lca(x,y)];
}int get(int x) { return x==dsu[x]?x:dsu[x]=get(dsu[x]); }int query(int x) {int p=get(x);return max(dis(x,l[p]),dis(x,r[p]));
}void insert(int x,int y) {x=get(x), y=get(y), dsu[x]=y;int a=l[x], b=r[x], c=l[y], d=r[y];if(dis(a,b)>dis(l[y],r[y])) l[y]=a, r[y]=b;if(dis(a,c)>dis(l[y],r[y])) l[y]=a, r[y]=c;if(dis(a,d)>dis(l[y],r[y])) l[y]=a, r[y]=d;if(dis(b,c)>dis(l[y],r[y])) l[y]=b, r[y]=c;if(dis(b,d)>dis(l[y],r[y])) l[y]=b, r[y]=d;
}signed main() {freopen("block.in","r",stdin);freopen("block.out","w",stdout);ios::sync_with_stdio(0);cin.tie(0);cin >> n >> m, T=__lg(n);up(i,1,n-1) {cin >> u[i] >> v[i];to[u[i]].pb(v[i]);to[v[i]].pb(u[i]);}up(i,1,m) {cin >> opt[i] >> id[i];if(opt[i]==1) del[id[i]]=1;}dfs(1,0);up(i,1,n) dsu[i]=l[i]=r[i]=i;up(i,1,n-1) if(!del[i]) insert(u[i],v[i]);dn(i,m,1) {if(opt[i]==1) { insert(u[id[i]],v[id[i]]); }else ans[i]=query(id[i])+1;}up(i,1,m) if(ans[i]) cout << ans[i]-1 << '\n';return 0;
}
军队
显然只关心每一行的 \(mi_i=\min\{男的,男娘\}\) ,小学奥数和一定差小积大,所以询问是对 \(mi\) 排序然后二分,就是你优先考虑 \(\lfloor\frac{y}{2}\rfloor\lceil\frac{y}{2}\rceil\),不然就让 \(mi\) 尽量小。
经典有脑扫描线,区间加减,查询区间 \(<k/\geq k\) 的数量,后面那个不好做做前面那个,考虑什么可以 pushup,发现可以维护区间最小的 \(10\) 个权值 ,顺便计数,然后就做完了。
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define fir first
#define sec second
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)using namespace std;const int N=300005, M=11;int n, m, c, tot, k, q, mi[N], tag[N<<2], lsy[N], lkx[N];
struct Seg { int pos, l, r, v; } arr[N<<1];
struct node { int len; pii u[M]; } tr[N<<2];inline void tup(int p) {int &len=(tr[p].len=0), i=1, j=1;while(len<10&&i<=tr[ls(p)].len&&j<=tr[rs(p)].len) {int l=tr[ls(p)].u[i].fir, r=tr[rs(p)].u[j].fir;if(l==r) {tr[p].u[++len]=mp(l,tr[ls(p)].u[i++].sec+tr[rs(p)].u[j++].sec);continue;}if(l<r) tr[p].u[++len]=tr[ls(p)].u[i++];else tr[p].u[++len]=tr[rs(p)].u[j++];}while(len<10&&i<=tr[ls(p)].len) tr[p].u[++len]=tr[ls(p)].u[i++];while(len<10&&j<=tr[rs(p)].len) tr[p].u[++len]=tr[rs(p)].u[j++];
} void build(int p=1,int s=1,int e=m) {tr[p].len=1, tr[p].u[1]=mp(0,e-s+1);if(s==e) return;int mid=(s+e)>>1;build(ls(p),s,mid), build(rs(p),mid+1,e);
}inline void add(node &p,int v) {up(i,1,p.len) p.u[i].fir+=v;
}void tdn(int p) {if(!tag[p]) return;tag[ls(p)]+=tag[p], add(tr[ls(p)],tag[p]);tag[rs(p)]+=tag[p], add(tr[rs(p)],tag[p]);tag[p]=0;
}void modify(int l,int r,int v,int p=1,int s=1,int e=m) {if(l<=s&&e<=r) {tag[p]+=v, add(tr[p],v);return;}tdn(p);int mid=(s+e)>>1;if(l<=mid) modify(l,r,v,ls(p),s,mid);if(r>mid) modify(l,r,v,rs(p),mid+1,e);tup(p);
}signed main() {freopen("army.in","r",stdin);freopen("army.out","w",stdout);ios::sync_with_stdio(0);cin.tie(0);cin >> n >> m >> c >> k >> q;while(c--) {int x, y, xx, yy;cin >> x >> y >> xx >> yy;arr[++tot]=(Seg){x,y,yy,1};arr[++tot]=(Seg){xx+1,y,yy,-1};}sort(arr+1,arr+1+tot,[](Seg i,Seg j){return i.pos<j.pos;});build();int j=1; up(i,1,n) {while(j<=tot&&arr[j].pos==i) {int l=arr[j].l, r=arr[j].r, v=arr[j].v;++j, modify(l,r,v); } up(j,1,tr[1].len) if(tr[1].u[j].fir<k) mi[i]+=tr[1].u[j].sec;mi[i]=min(mi[i],m-mi[i]);}sort(mi+1,mi+1+n,[](int i,int j){return i>j;});up(i,1,n) lsy[i]=lsy[i-1]+mi[i], lkx[i]=lkx[i-1]+mi[i]*mi[i];while(q--) {int cnt, res, l=1, r=n, p=0;cin >> cnt >> res;while(l<=r) {int mid=(l+r)>>1;if(mi[mid]>=res/2) p=mid, l=mid+1;else r=mid-1;}if(p>=cnt) cout << (res/2)*((res+1)/2)*cnt << '\n';else {int ans=(res/2)*((res+1)/2)*p;ans+=(lsy[cnt]-lsy[p])*res-(lkx[cnt]-lkx[p]);cout << ans << '\n'; }}return 0;
}