luogu 记录
luogu 记录
P8215 分组作业
题面:
老师布置了分组作业。在此之前,老师将班上 \(2n\) 个学生分成了 \(n\) 组,每组两个人。其中 \(1\) 号和 \(2\) 号为一组,\(3\) 号和 \(4\) 号为一组,……,\(2n-1\) 号和 \(2n\) 号为一组。
老师让每个队伍自行安排分工。这样是否合作就成了一个大问题。大家决定用表决的方式来确定。首先每个人决定是否愿意和队友合作。不同的人因为自己的原因和分配的队友的原因,对合作的意愿不一样,对于第 \(i\) 个学生,选择“愿意”会产生 \(c_i\) 的不满,选择“不愿意”会产生 \(d_i\) 的不满。
如果两名队友都选择“愿意”,那么根据实际情况他们可以合作或者不合作。但是如果有一名队友选择“不愿意”,那么他们只能不合作。
学生中还有 \(m\) 个单向的喜欢关系,一个关系形如“\(A\) 喜欢 \(B\)”。在这样一个关系中,如果 \(A\) 没有和队友合作,且 \(B\) 选择了“愿意”,\(A\) 会有略微沮丧,产生 \(a_i\) 的不满;如果 \(A\) 表决了“不愿意”,但 \(B\) 成功与队友合作,那么 \(A\) 会羡慕嫉妒恨并产生 \(b_i\) 的不满。(由于当 \(A\) 和 \(B\) 在同一组时这种设定会变得很奇怪,所以题目保证不会有这种情况)其中 \(i\) 表示第 \(i\) 个关系。
如果一个学生 \(i\) 选择了“愿意”但是他的队友选择了“不愿意”,那么他会因为队友产生 \(e_i\) 的不满。
问所有情况下最小的不满之和是多少。
\(1\le n \le 5000\),\(0\le m \le 10000\),\(1\le a_i,b_i,c_i,d_i,e_i\le 10^9\)。
题解:
智慧的网络流建图题。
套路的,每个学生都是一个点 \(i\),他有两种选择,都需要付出相应的代价,转化为最小割问题,割到 \(S\) 点集或是 \(T\) 点集分别对应两种选择。即建立边 \((S,i,d_i)\) 和 \((i,T,c_i)\) 我们分别称之为 \(d\) 类边和 \(c\) 类边。
之后再建立对每一组同学 \((i,i+1)\) 建边 \((i,i+1,e_i)\) 和 \((i+1,i,e_{i+1})\)(称之为 \(e\) 类边),表示如果两个人选择状态不一样就会有经过对应 \(e\) 边的 \(S\) 可达 \(T\) 的路径,那就还需要割掉某个 \(e\) 边。
最高妙的地方就是如何处理“喜欢”的限制。
对每组建立点 \(x_i\) 对应组 \((2i-1,2i)\)。若割断后 \(S\) 能到达 \(x_i\) 表示 \(i\) 组选择合作,否则表示不合作。然后建边 \((x_i,2i-1,inf),(x_i,2i,inf)\) (称之为 \(i\) 类边)。
对于每个关系 \(A\) “喜欢” \(B\),建边 \((B,x_{\lceil A/2\rceil},a_i),(x_{\lceil B/2\rceil},A,b_i)\) 称之为 \(a\) 类边和 \(b\) 类边。
正确性可以分类讨论证明。
首先对于 \(a\) 边 \((i,x_j,a_k)\),如果 \(x_j\) 里的组员 \(y\) 选择了不愿意,而 \(i\) 选择了愿意,那么就会有路径 \(S\to i\to x_j\to y\to T\),所以 \(a_k\) 是一定会被割掉的。(不可能不割掉 \(a_k\),如果 \(a_k\) 不割那么由于 \(i\) 边的存在 \(c_{2j-1}\) 和 \(c_{2j}\) 都会割掉,那么 \(y\) 选择不愿意时的 \(d_y\) 就不用割了。)
若一个组 \(x_i\) 只有 \(b\) 边,那么不管组员怎么选都肯定不合作,答案不会变劣,对应到图上即 \(x_i\) 没有入边,所以 \(S\) 也必然不能到达 \(x_i\)。
若一个组 \(x_i\) 只有 \(a\) 边,上述论证了如果有人不愿意那 \(a_k\) 肯定会割掉,如果都愿意那选择合作答案肯定不会变劣,这对应了如果两条 \(c\) 边全部割掉那一定没有 \(S\) 经 \(x_i\) 可达 \(T\) 的路径,所以 \(a\) 边也不用割了。
若一个组 \(x_i\) 同时有 \(a,b\) 两种边,那么他割掉的边中肯定不会同时有 \(a,b\) 两种边,这肯定不如把所有 \(a\) 边全割掉或把所有 \(b\) 边全割掉更优。此时若他割掉所有 \(a\) 边表示 \(x_i\) 组不合作,反之表示合作。这两种情况正好分别对应了题目中“喜欢”的两种惩罚状态。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
int s=0,k=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-') k=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
s=(s<<3)+(s<<1)+(c^48);
c=getchar();
}
return s*k;
}
const int N=5005,M=10005,inf=1e9+7;
int n,m,S,T,pos[N*2],dep[N*3],E[N*3],head[N*3],cnt=1;
struct edge{
int v,nxt,w;
}e[N*8+M*2<<1];
void add(int u,int v,int w){
e[++cnt]={v,head[u],w};
head[u]=cnt;
e[++cnt]={u,head[v],0};
head[v]=cnt;
}
bool bfs(){
for(int i=S;i<=T;i++) dep[i]=0,E[i]=head[i];
queue<int>q; q.push(S); dep[S]=1;
while(!q.empty()){
int x=q.front(); q.pop();
for(int i=head[x],v;i;i=e[i].nxt){
v=e[i].v;
if(e[i].w&&!dep[v]){
dep[v]=dep[x]+1;
q.push(v);
}
}
}
return dep[T]!=0;
}
int dfs(int x,int W){
if(x==T) return W;
int now=0;
for(int i=E[x],v;i&&now<W;i=e[i].nxt){
E[x]=i; v=e[i].v;
if(e[i].w&&dep[v]==dep[x]+1){
int tmp=dfs(v,min(e[i].w,W-now));
if(!tmp) dep[v]=-1;
else{
now+=tmp;
e[i].w-=tmp;
e[i^1].w+=tmp;
if(now==W) return W;
}
}
}
return now;
}
ll dinic(){
ll ans=0;
while(bfs()) ans+=dfs(S,inf);
return ans;
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n=read();m=read();
S=0;T=n*3+1;
for(int i=2;i<=n<<1;i+=2) pos[i-1]=pos[i]=n*2+i/2;
for(int i=1;i<=n<<1;i++){
add(pos[i],i,inf);
int A=read(),B=read(),C=read();
add(S,i,B); add(i,T,A);
add(i,(i-1^1)+1,C);
}
for(int i=1;i<=m;i++){
int u=read(),v=read(),A=read(),B=read();
int x=pos[u],y=pos[v];
add(y,u,B); add(v,x,A);
}
printf("%lld",dinic());
return 0;
}
P4548 歌唱王国
题面:
在歌唱王国,所有人的名字都是一个非空的仅包含整数 \(1\sim n\) 的字符串。
王国里生活着一大群咕噜兵,他们靠不停地歌唱首领——牛人酋长们的名字来获取力量。咕噜兵每一次歌唱过程是这样的:首先,他从整数生成器那儿获得一个数字,然后花一个时间单位将此数字唱出来,如果他发现某个牛人酋长的名字已经被歌唱出来(即此名字是歌唱序列的一个连续子串),那么这次歌唱过程就立即结束。
相关名词定义:
- 歌唱序列:如果某人歌唱了 \(x\) 个数字,第 \(i\) 次歌唱的数字为 \(a_i\),那么歌唱序列 \(=(a_1,a_2,\cdots,a_x)\)。
- 整数生成器:歌唱王国的神物,它有一个按钮,如果你按一下按钮,将从 \(1\sim n\) 数字中等概率的随机返回一个整数。
- 歌唱时间:在一次歌唱过程中花费的时间。
歌唱时间是随机的,无法预料;不过歌唱时间的期望值是固定的,此期望值即平均来说歌唱时间有多长,亦可称作平均歌唱时间。
王国里的人非常喜欢歌唱,他们希望歌唱的时间越长越好,所以他们决定罢免一些牛人酋长,使得平均歌唱时间变长。但是他们不能罢免掉所有的牛人酋长,否则他们每次歌唱都无法停止,无法获取力量;于是他们决定只保留一个牛人酋长而罢免其余的牛人酋长。
你的任务是:对于给定的 \(n\)、牛人酋长的个数 \(t\) 以及每一个牛人酋长的名字,告诉王国里的人们,对于 \(1\leq i\leq t\),如果保留第 \(i\) 个牛人酋长,罢免掉其余的,那么平均歌唱时间将是多少。
提示:此数为一个非负整数!
输出要求:由于这个数字太大,所以你只需输出这个数的末 \(4\) 位数字。如果不足 \(4\) 位,则前面补 \(0\)。\(1\leq n\leq 10^5\),\(t\leq 50\),\(1\leq m_i\leq 10^5\)。
题解:
题目即要求对于每个模式串求在文本串中第一次出现的期望长度。
考虑 \(kmp\) 的匹配过程,设当前模式串 \(s\) 长度为 \(k\),\(f(i)\) 表示文本串当前匹配到了模式串的第 \(i\) 个字符之后直到结束还需要的期望步数。初始 \(f(k)=0\)。
有转移方程 \(f(i)=1+\frac 1 n\sum_{c=1}^nf(tr(i,c))\),\(tr(i,c)\) 表示前 \(i\) 个字符已经匹配上之后在加入一个字符 \(c\) 会匹配到模式串的哪个位置。即若 \(c=s_{i+1}\),那么 \(tr(i,c)=i+1\),否则 \(tr(i,c)=tr(nxt_i,c)\)。
注意到 \(tr(i,c)\) 和 \(tr(nxt_i,c)\) 的结果只有当 \(c=s_{i+1}\) 时不同,所以设 \(j=nxt_i\) 有 \(f(j)-\frac 1 nf(tr(j,s_{i+1}))=f(i)-\frac 1 nf(tr(i,s_{i+1}))\)。
因为 \(tr(i,s_{i+1})=i+1,tr(nxt_i,s_{i+1})=nxt_{i+1}\) 化简得 \(f(nxt_i)-f(i)=\frac 1 n(f(nxt_{i+1})-f(i+1))\)。
设 \(g(i)=f(nxt_i)-f(i)\),有转移 \(g(i)=\frac{g(i+1)} n\),边界是 \(g(1)=f(0)-f(1)\)。根据 \(f\) 的转移方程有 \(f(0)=1+\frac{n-1} n f(0)+\frac 1 n f(1)\),即 \(f(0)-f(1)=n\),所以 \(g(1)=n\),\(g(i)=n^i\)。
因为 \(f(k)=0\),所以 \(g(k)=f(nxt_k)=n^k\),同理 \(f(nxt_{nxt_k})=n^k+n^{nxt_k}\)。所以答案是从 \(k\) 开始跳 \(nxt\),\(n\) 的沿途位置次幂加和。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
ll s=0,k=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-') k=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
s=(s<<3)+(s<<1)+(c^48);
c=getchar();
}
return s*k;
}
const int N=1e5+5,mod=10000;
int a[N],nxt[N];
ll fac[N];
void Add(ll &x,ll y){
x+=y;
if(x>=mod) x-=mod;
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
fac[0]=1; fac[1]=read();
for(int i=2;i<N;i++) fac[i]=fac[i-1]*fac[1]%mod;
int T=read();
while(T--){
int n=read();
for(int i=1;i<=n;i++) a[i]=read();
nxt[1]=0;
for(int i=2,j=0;i<=n;i++){
while(j&&a[i]!=a[j+1]) j=nxt[j];
if(a[i]==a[j+1]) j++;
nxt[i]=j;
}
ll ans=0;
for(int i=n;i>=1;i=nxt[i]) Add(ans,fac[i]);
printf("%04lld\n",ans);
}
return 0;
}