T1 这把我们就遇到了高手了
题目描述
X
∗
Y
X*Y
X∗Y 的环形网格(左边和右边连通,上边和下边连通)。
给出
n
n
n 个矩形的左下和右上点对,每个矩形可以选择四种方式之一摆放:
求
n
n
n 个矩形能同时覆盖的最大面积。
样例:
题目分析
乍一看非常不可做。
仔细思考/打完暴力 感觉这个矩形的形式非常的美妙。恰好是4种方式,横纵坐标各2种。
这启示我们可以把横纵坐标分开看,然后把两个的最优答案乘起来。
然后问题就变成了一维的,每个矩形变成一条线段,把数轴划分成很多段。
那么每段有一个状态,对应每个矩形的线段取的是里面还是外面。
状态相同的就可以同时取到。
那么给这个 01 串设个哈希值,哈希值相同的段就加起来,取最大长度即可。
从左往右扫描即可,可以用map,更快的做法是得到每段哈希值后再按哈希值排序。
Code:
#include<bits/stdc++.h>
#define maxn 500005
#define y1 y_1
#define LL long long
#define ULL unsigned long long
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
void read(int &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,X,Y,x1[maxn],y1[maxn],x2[maxn],y2[maxn];
ULL pw[maxn];
struct node{
int x; ULL v;
bool operator < (const node &p)const{return x<p.x;}
}q[maxn*2]; int cnt;
map<ULL,int>val;
int solve(){
ULL H=0; val.clear();
int ret=0;
sort(q+1,q+1+cnt);
rep(i,0,cnt-1){
H+=q[i].v;
ret=max(ret,val[H]+=q[i+1].x-q[i].x);
}
return ret;
}
int main()
{
freopen("master.in","r",stdin);
freopen("master.out","w",stdout);
read(n),read(X),read(Y);
rep(i,1,n) read(x1[i]),read(y1[i]),read(x2[i]),read(y2[i]);
rep(i,pw[0]=1,n) pw[i]=pw[i-1]*137;
q[cnt=1]=(node){X,0};
rep(i,1,n) q[++cnt]=(node){x1[i],pw[i]},q[++cnt]=(node){x2[i],-pw[i]};
int ansx = solve();
q[cnt=1]=(node){Y,0};
rep(i,1,n) q[++cnt]=(node){y1[i],pw[i]},q[++cnt]=(node){y2[i],-pw[i]};
int ansy = solve();
printf("%lld\n",1ll*ansx*ansy);
}
T2 ok起飞
题目描述
一棵仙人掌,有边权,设 f ( s , t ) f(s,t) f(s,t) 为 s → t s\to t s→t 的最小割,求 ∑ 1 ≤ s < t ≤ n f ( s , t ) ⊕ s ⊕ t \sum_{1\le s<t\le n}f(s,t)\oplus s\oplus t ∑1≤s<t≤nf(s,t)⊕s⊕t
n ≤ 1 0 5 , w i ≤ 1 0 6 n\le 10^5,w_i\le 10^6 n≤105,wi≤106
题目分析
先考虑树的情况,显然是先取最小的边把两边分开,然后递归,但是这样不好实现,可以从大到小加入边然后用并查集合并,由于编号要取异或,把二进制位分开统计,记录连通块内第 k k k 位为 0/1 的点的个数,然后根据边权第 k k k 位的情况统计权值。
仙人掌的话,如果最小割落在环上,那么必然会割掉两条边,并且环上的最小边一定会被割,那么把最小边删掉,把它的权值加到环上的其它边上,最小割不会改变。
于是就变成了树的情况。
复杂度 O ( n log V ) O(n\log V) O(nlogV)
Code:
#include<bits/stdc++.h>
#define maxn 100005
#define maxm 400005
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
void read(int &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int T,n,m;
int fir[maxn],nxt[maxm],to[maxm],w[maxm],tot=1;
void line(int x,int y,int z){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,w[tot]=z;}
int dfn[maxn],low[maxn],tim,stk[maxn*2],top;
struct edge{
int x,y,w;
bool operator < (const edge &p)const{return w>p.w;}
}e[maxn]; int cnt;
void tarjan(int u,int ff){
dfn[u]=low[u]=++tim;
for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff){
if(!dfn[v]){
stk[++top]=i;
tarjan(v,u),low[u]=min(low[u],low[v]);
if(dfn[u]<low[v]) e[++cnt]=(edge){u,v,w[i]},top--;
else if(dfn[u]==low[v]){
int mn=1e9,id;
for(int t=-1,j=top;t!=i;) t=stk[j--],mn>w[t]&&(mn=w[t],id=t);
for(int t=-1;t!=i;) t=stk[top--],id!=t&&(e[++cnt]=(edge){to[t^1],to[t],w[t]+mn},0);
}
}
else if(dfn[v]<dfn[u]) stk[++top]=i,low[u]=min(low[u],dfn[v]);
}
}
int f[maxn],s[maxn][21][2];
int find(int x){return !f[x]?x:f[x]=find(f[x]);}
int main()
{
freopen("okfly.in","r",stdin);
freopen("okfly.out","w",stdout);
read(T);
while(T--){
read(n),read(m);
memset(fir,0,(n+1)<<2),tot=1;
memset(dfn,0,(n+1)<<2),tim=top=0;
cnt=0;
for(int i=1,x,y,z;i<=m;i++) read(x),read(y),read(z),line(x,y,z),line(y,x,z);
tarjan(1,0);
long long ans=0;
sort(e+1,e+1+cnt);
//for(int i=1;i<=cnt;i++) cout<<e[i].x<<' '<<e[i].y<<' '<<e[i].w<<endl;
for(int i=1;i<=n;i++){
f[i]=0;
for(int j=0;j<=20;j++) s[i][j][0]=s[i][j][1]=0,s[i][j][i>>j&1]++;
}
for(int i=1;i<=cnt;i++){
int x=find(e[i].x),y=find(e[i].y);
for(int k=0;k<=20;k++){
int w=e[i].w>>k&1;
for(int l=0;l<2;l++) ans+=1ll*s[x][k][l]*s[y][k][l^w^1]*(1<<k);
}
f[y]=x;
for(int k=0;k<=20;k++) s[x][k][0]+=s[y][k][0],s[x][k][1]+=s[y][k][1];
}
printf("%lld\n",ans);
}
}
T3 这钵和餐厅配合的不是很好
题目描述
长度为 n n n 的序列 a i a_i ai, n ≤ 200 , 1 ≤ a i ≤ 1000 n\le 200,1\le a_i\le 1000 n≤200,1≤ai≤1000
对于一个排列 p p p,令 b i = a p i , s i = ∑ j = 1 i b j b_i=a_{p_i}, s_i=\sum_{j=1}^ib_j bi=api,si=∑j=1ibj,贡献为 1 ∏ i = 2 n s i \frac 1{\prod_{i=2}^ns_i} ∏i=2nsi1
求所有排列的贡献之和。
题目分析
这篇 blog 讲得蛮好。
介绍一下硬上的做法(差不太多):
令 m = ∑ a i m=\sum a_i m=∑ai
考虑 m m m 个带标号的球,第 i i i 种颜色的有 a i a_i ai 个,随便排列。
那么每种颜色中编号最大的球位置也最大的概率就是 1 ∏ a i \frac 1{\prod a_i} ∏ai1 。(概率乘上 n ! n! n! 就是方案数了,这么说后面好理解一点)
考虑一个排列 p p p,表示每种颜色的最大位置的顺序(此后都要求最大编号在最大位置),那么形成这个排列方式的概率就是 1 ∏ i = 1 n s i \frac 1{\prod_{i=1}^n s_i} ∏i=1nsi1 ( p n p_n pn 表示最大)
而现在我们想求
∑
p
1
∏
i
=
2
n
s
i
\sum_p \frac 1{\prod_{i=2}^ns_i}
∑p∏i=2nsi1,相当于最大位置最小的那个颜色不要求编号最大的在最大位置。
那么我们枚举这个颜色
x
x
x,剩下的颜色只需要满足最大编号在最大位置,且最大位置大于
x
x
x 的最大位置,求满足条件的摆放的生成概率。
第一个限制只需要除上一个 ∏ i ≠ x a i \prod_{i\neq x} a_i ∏i=xai,第二个限制可以钦定有多少种颜色的最大位置小于 x x x 的最大位置来容斥,假设这个颜色集合是 S S S,那么贡献就是 ( − 1 ) ∣ S ∣ ∗ a x a x + ∑ i ∈ S a i (-1)^{|S|}*\frac {a_x}{a_x+\sum_{i\in S}a_i} (−1)∣S∣∗ax+∑i∈Saiax,后面一项是 x x x 颜色在最大位置的概率。
枚举 ∑ a i \sum a_i ∑ai 来计算,那么就只需要知道 ∑ a i \sum a_i ∑ai 对应的 ∑ ( − 1 ) S \sum (-1)^S ∑(−1)S,就是一个背包,可以写成生成函数 ( 1 − x a i ) (1-x^{a_i}) (1−xai),算某种颜色时除去对应项即可。
复杂度 O ( n ∗ m ) = O ( n 2 max a i ) O(n*m)=O(n^2\max a_i) O(n∗m)=O(n2maxai)
Code:
#include<bits/stdc++.h>
#define maxn 205
#define maxm 205*1005
using namespace std;
const int mod = 998244353;
int n,a[maxn],m,F[maxm],G[maxm],inv[maxm],Inv=1,ans;
int main()
{
freopen("restaurant.in","r",stdin);
freopen("restaurant.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
F[0]=1;
for(int i=1;i<=n;i++)
for(int j=(m+=a[i]);j>=a[i];j--)
F[j]=(F[j]-F[j-a[i]])%mod;
inv[0]=inv[1]=1;
for(int i=2;i<=m;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=1;i<=n;i++) Inv=1ll*Inv*inv[a[i]]%mod;
for(int i=1;i<=n;i++){
int s=0;
for(int j=0;j<=m-a[i];j++) G[j]=(F[j]+(j>=a[i]?G[j-a[i]]:0))%mod, s=(s+1ll*G[j]*inv[j+a[i]])%mod;
s=1ll*s*a[i]%mod*a[i]%mod*Inv%mod;
ans=(ans+s)%mod;
}
printf("%d\n",(ans+mod)%mod);
}