好久没更了

写点东西吧= =

A 01Matrix

简单构造

左上角和右下角染成1其他染成0即可

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int a[1010][1010];
int main(){
int n,m,A,B;
cin>>n>>m>>A>>B;
if(A*2>m||B*2>n)return puts("-1"),0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(i<=B&&j<=A)putchar('1');
else if(i>B&&j>A)putchar('1');
else putchar('0');
if(j==m)puts("");
}
return 0;
}

B Sorting a Segment

考虑若(L,R)和(L',R')排序后相同

则 $ \forall $ L<L''<L',(L,R)和(L'',R'')排序后相同

因此只需处理相邻两个是否排序后是否相同即左右两端点是否恰是最值即可

注意若两个区间排序后不改变任何数位置需要特殊处理

形式化的,我们先拉出所有排序后不改变位置的区间然后数前面相邻相同的数量即可

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int a[200010],b[2000010],n,m;
int qzl[200010],qzr[200010],hzl[200010],hzr[200010];
int calc(int L,int R){
return a[L]==min(hzl[L],qzl[R])&&a[R]==max(hzr[L],qzr[R]);
}
int main(){
scanf("%d%d",&n,&m);
int tag=-1;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
if(i>0&&a[i]>a[i-1])b[i]=b[i-1]+1;
else b[i]=1;
if(b[i]>=m)tag++;
}
for(int i=0;i<n;i++){
if(i%m==0)qzl[i]=qzr[i]=a[i];
else qzl[i]=min(qzl[i-1],a[i]),qzr[i]=max(qzr[i-1],a[i]);
}
for(int i=n-1;i>=0;i--){
if(i==n-1||(i+1)%m==0)hzl[i]=hzr[i]=a[i];
else hzl[i]=min(hzl[i+1],a[i]),hzr[i]=max(hzr[i+1],a[i]);
}
int ans=n-m+1;
for(int i=m-1;i<n-1;i++)
if(b[i]<m&&b[i+1]<m&&calc(i-m+1,i+1))ans--;
ans-=max(0,tag);cout<<ans;
return 0;
}

C LCMs

简单数论题

\[\begin{aligned}
&\sum_{i=1}^n\sum_{j=i+1}^n\frac{a_ia_j}{\gcd(a_i,a_j)}\\
&=\sum_{d=1}^{max}\frac{1}{d}\sum_{i=1}^n[d|a_i]\sum_{j=i+1}^n[d|a_j]a_ia_j[\gcd(a_i,a_j)=1]\\
&=\sum_{d=1}^{max}\frac{1}{d}\sum_{i=1}^n[d|a_i]\sum_{j=i+1}^n[d|a_j]a_ia_j\sum_{k|a_i,k|a_j}\mu(k)\\
&=\sum_{d=1}^{max}\frac{1}{d}\sum_{i=1}^n[d|a_i]\sum_{j=i+1}^n[d|a_j]a_ia_j\sum_{k|a_i,k|a_j}\mu(k)\\
&=\sum_{d=1}^{max}\frac{1}{d}\sum_{k=1}^{\frac{max}{d}}\mu(k)(\sum_{i=1}^n[dk|a_i]\sum_{j=i+1}^n[dk|a_j]a_ia_j)\\
\end{aligned}
\]

右边部分显然是关于dk的一个函数 预处理就做完了

#include<bits/stdc++.h>
#define ll long long
#define p 998244353
#define inv2 499122177
using namespace std;
int mu[1000010],ss[1000010],cnt;bool pri[1000010];
int a[200010],tong[1000010];
int h[1000010],f[1000010];
int n,m,k;
int inv[1000010];
void init(int N){
mu[1]=1;
for(int i=2;i<=N;i++){
if(!pri[i])ss[++cnt]=i,mu[i]=-1;
for(int j=1;i*ss[j]<=N&&j<=cnt;j++){
pri[i*ss[j]]=1;
if(i%ss[j]==0){
mu[i*ss[j]]=0;
break;
}
mu[i*ss[j]]=-mu[i];
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),tong[a[i]]++;
inv[1]=1;
for(int i=2;i<=1000000;i++)inv[i]=1ll*inv[p%i]*(p-p/i)%p;
init(1000000);
for(int i=1;i<=1000000;i++){
for(int j=i;j<=1000000;j+=i)(h[i]+=1ll*tong[j]*j%p)%=p,
(f[i]+=1ll*j*tong[j]%p*j%p)%=p;
}
for(int i=1;i<=1000000;i++)
f[i]=1ll*((1ll*h[i]*h[i]%p-f[i])%p+p)%p*inv2%p;
ll ans=0;
for(int k=1;k<=1000000;k++)
for(int T=k;T<=1000000;T+=k)
(ans+=1ll*inv[k]*mu[T/k]%p*f[T]%p)%=p;cout<<ans;
return 0;
}

D Unique Path

先考虑所有"只能有一条路径"的对,这两个点显然在同一联通块中

且所有联通块不能出现环 即类似克鲁斯卡尔的加边

然后再考虑所有"有多条路径"的对,如果这个点对在同一联通块内则GG

因为这会导致连通块内出现环即导致"只能有一条路径"的对GG

然后发现任意两个联通块之间只可能有一条边,否则会出现环

计算m是否超过能插入的最大边数即可

#include<bits/stdc++.h>
int n,k,a,b,c,ds,fa[200010],L[200010],R[200010],d;
int ask(int x){return x==fa[x]?x:fa[x]=ask(fa[x]);}
long long m;
int main(){
scanf("%d%lld%d",&n,&m,&k);ds=n;
for(int i=0;i<n;i++)fa[i]=i;
while(k--){
scanf("%d%d%d",&a,&b,&c);
int p=ask(a),q=ask(b);
if(c==0&&p!=q)ds--,fa[q]=p;
if(c==1)L[++d]=a,R[d]=b;
}
for(int i=1;i<=d;i++)if(ask(L[i])==ask(R[i]))return puts("No"),0;
puts((m+ds-n>1ll*ds*(ds-1)/2)||(d&&(ds==2||m<n))?"No":"Yes");
return 0;
}

E Gachapon

神仙题

考虑minmax反演,转换为了求每个子集的“存在一个元素 $ i $ 出现次数超过 $ b_i $” 期望次数

期望次数等价于所有“不存在一个元素 $ i $ 出现次数超过 $ b_i $” 的状态的期望出现次数之和

因为假如某个操作序列经过q次走出去了,前q个前置状态都相当于没走出去

而每个没走出去的状态的出现次数等价于出现概率乘上 $ \frac{S}{U} $ 其中U是当前子集的a的总和

这是因为到达该状态之后只要接下来选中的元素在集合外都会使得出现次数++

等比数列收敛后可得到上述结论

接下来我们只需要计算每个状态的出现概率 这样我们可以暂时忽略走到子集外的情况

我们枚举每个子集,考虑枚举操作序列的长度(不超过 $ \sum b_i $ )并计算这样的操作序列的数量

容易发现这可以由指数生成函数 $ E_d(x)=\sum\limits_{i=0}{B_d-1}\frac{xi(\frac{A_d}{U})^i}{i!}$ 代替

其中 $ x^i $ 的系数就是操作序列长度为 $ i$ 的答案

枚举子集复杂度显然GG 我们注意到我们没有必要枚举子集,只需要枚举U即可

然后做类似背包的DP,每次转移进行多项式乘法(这里可以暴力乘)

就做完了... 感觉讲的很不清楚啊... 还有问题的话私信我吧...(没有语言表达能力)

#include<bits/stdc++.h>
#define ll long long
#define p 998244353
using namespace std;
int a[1010],b[1010];
int jc[1010],njc[1010],inv[1010];
vector<int>ret,E[1010],all[1010];
vector<int>mul(vector<int>x,vector<int>y){
vector<int>ret;
int n=x.size(),m=y.size();
ret.resize(n+m-1);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
(ret[i+j]+=1ll*x[i]*y[j]%p)%=p;return ret;
}
void del(vector<int>&x,vector<int>y){
int n=x.size(),m=y.size();
x.resize(max(n,m));
for(int i=0;i<m;i++)(x[i]-=y[i])%=p;
}
void add(vector<int>&x,vector<int>y){
int n=x.size(),m=y.size();
x.resize(max(n,m));
for(int i=0;i<m;i++)(x[i]+=y[i])%=p;
}
int main(){
int n;scanf("%d",&n);
for(int i=0;i<2;i++)jc[i]=njc[i]=inv[i]=1;
for(int i=2;i<=405;i++){
jc[i]=1ll*jc[i-1]*i%p;
inv[i]=1ll*inv[p%i]*(p-p/i)%p;
njc[i]=1ll*njc[i-1]*inv[i]%p;
}
for(int i=0;i<n;i++)scanf("%d %d",&a[i],&b[i]);
int S=0,ans=0;
for(int i=0;i<n;i++)S+=a[i];
for(int i=0;i<n;i++){
for(int k=0,P=1;k<b[i];k++,P=1ll*P*a[i]%p)E[i].push_back(1ll*P*njc[k]%p);
}
for(int i=0;i<n;i++){
for(int k=S-a[i];k>=1;k--)if(all[k].size())
del(all[a[i]+k],mul(E[i],all[k]));
add(all[a[i]],E[i]);
}
for(int i=1;i<=S;i++){
for(int j=0,u=inv[i];j<all[i].size();j++,u=1ll*u*inv[i]%p)
(ans+=1ll*all[i][j]*u%p*jc[j]%p)%=p;
}
(ans+=p)%=p;
cout<<1ll*ans*S%p;
return 0;
}

F Two Permutations

比E清真多了...

考虑两个排列的每个环上,所有点要么一起动要么一起不动

因此就变成了以下问题:

  • 每个环可以选择动(1)或不动(0)
  • 两个环若一起动或一起不动会有一个代价
  • 某个环若动或不动会有一个代价
  • 要求代价最小值

这是经典的二元关系最小割 这里有

注意一点细节如把第二个序列改成动(0)或不动(1)这样就是两个环若不同则有代价

注意这张图是二分图 直接dinic就好了 复杂度是$ O(n \sqrt{n})$的

ISAP好像过不去..?

#include<bits/stdc++.h>
#define ll long long
#define M 400010
using namespace std;
int n,k,cnt,S,T,h,t;
int F[M],L[M],N[M],a[M],c[M],Gap[M],dis[M],q[M],cur[M];
void add(int x,int y,int z,int fla=0){
a[++k]=y;c[k]=z;
N[k]=F[x];F[x]=k;
if(!fla)add(y,x,0,1);
}
bool BFS(int x,int y){
q[t=1]=x;h=0;
for(int i=1;i<=n;i++)cur[i]=F[i],dis[i]=1000000000;
dis[x]=0;
while(h<t){
x=q[++h];
for(int i=F[x];i;i=N[i])if(c[i]&&dis[a[i]]>n){
dis[a[i]]=dis[x]+1;
if(a[i]==y)return 1;
q[++t]=a[i];
}
}
return dis[y]<=n;
}
int dfs(int x,int T,int flow){
if(x==T)return flow;
int used=0;
for(int i=cur[x];i;i=cur[x]=N[i])if(c[i]&&dis[a[i]]==dis[x]+1){
const int v=dfs(a[i],T,min(flow-used,c[i]));
if(!v)continue;c[i]-=v;c[i^1]+=v;used+=v;
if(used>=flow)return used;
}
return used;
}
int dinic(int S,int T){
int ans=0;
while(BFS(S,T))ans+=dfs(S,T,1000000000);
return ans;
}
int A[200010],B[200010],P[200010],Q[200010],cnt1;
int main(){
scanf("%d",&n);k=1;
for(int i=1;i<=n;i++)scanf("%d",&A[i]),A[i]++;
for(int i=1;i<=n;i++)scanf("%d",&B[i]),B[i]++;
for(int i=1;i<=n;i++)if(!P[i]){
P[i]=++cnt1;
for(int x=A[i];!P[x];x=A[x])P[x]=cnt1;
}
for(int i=1;i<=n;i++)if(!Q[i]){
Q[i]=++cnt1;
for(int x=B[i];!Q[x];x=B[x])Q[x]=cnt1;
}
int all=n;
S=cnt1+1;T=cnt1+2;
for(int i=1;i<=n;i++){
if(A[i]==B[i]){
if(A[i]==i)all--;
else add(P[i],Q[i],1),add(Q[i],P[i],1);
}
else {
if(A[i]==i)add(Q[i],T,1);
else if(B[i]==i)add(S,P[i],1);
else add(Q[i],P[i],1);
}
}
n=T;
cout<<all-dinic(S,T);
return 0;
}

AtCoder Grand Contest 038题解的更多相关文章

  1. AtCoder Grand Contest 038 题解

    传送门 这场表现的宛如一个\(zz\) \(A\) 先直接把前\(b\)行全写成\(1\),再把前\(a\)列取反就行 const int N=1005; char mp[N][N];int n,m, ...

  2. AtCoder Grand Contest 038 简要题解

    从这里开始 比赛目录 Problem A 01 Matrix Code #include <bits/stdc++.h> using namespace std; typedef bool ...

  3. AtCoder Grand Contest 017 题解

    A - Biscuits 题目: 给出 \(n\) 个物品,每个物品有一个权值. 问有多少种选取方式使得物品权值之和 \(\bmod\space 2\) 为 \(p\). \(n \leq 50\) ...

  4. Atcoder Grand Contest 038 F - Two Permutations(集合划分模型+最小割)

    洛谷题面传送门 & Atcoder 题面传送门 好久前做的题了--今天偶然想起来要补个题解 首先考虑排列 \(A_i\) 要么等于 \(i\),要么等于 \(P_i\) 这个条件有什么用.我们 ...

  5. Atcoder Grand Contest 054 题解

    那天晚上由于毕业晚会与同学吃饭喝酒没打 AGC,第二天稍微补了下题,目前补到了 E,显然 AGC 的 F 对于我来说都是不可做题就没补了(bushi A 简单题,不难发现如果我们通过三次及以上的操作将 ...

  6. AtCoder Grand Contest 030题解

    第一次套刷AtCoder 体验良好 传送门 Poisonous Cookies cout<<b+min(c,a+b+); Tree Burning 难度跨度有点大啊 可以证明当第一次转向之 ...

  7. AtCoder Grand Contest 031题解

    题面 传送门 题解 比赛的之后做完\(AB\)就开始发呆了--简直菜的一笔啊-- \(A - Colorful\ Subsequence\) 如果第\(i\)个字母选,那么它前面任意一个别的字母的选择 ...

  8. AtCoder Grand Contest 039 题解

    传送门 \(A\) 首先只有一串的情况下,遇到相同的肯定是改后面那一个最优,然后两串的话可能要分奇偶讨论一下 //quming #include<bits/stdc++.h> #defin ...

  9. AtCoder Grand Contest 017题解

    传送门 \(A\) 直接转移就是了 typedef long long ll; const int N=55; ll f[N][2];int a[N],n,p; int main(){ scanf(& ...

随机推荐

  1. 国家虚拟仿真实验教学项目共享平台(实验空间)PHP SDK

    使用XJWT标准,此标准基于JSON Web Token (JWT)开发.XJWT包含三个参数:header, payload, signature,因此生成token就要先获得这三个参数. clas ...

  2. OCC与MVCC 的区别

    一.前言 在数据库中,并发控制是指在多个用户/进程/线程同时对数据库进行操作时,如何保证事务的一致性和隔离性的,同时最大程度地并发. 当多个用户/进程/线程同时对数据库进行操作时,会出现3种冲突情形: ...

  3. dedecms5.7执行PHP代码的用法

    dedecms5.7执行PHP代码的用法 {dede:php} echo 'test'; {/dede:php}

  4. C# ??(两个问号)的表达式使用详解

    今天有人问我C#中两个问号是什么意思,怎么使用,于是乎有了这篇随笔 有时候我们需要判断某个对象是否为null,一般的做法是 if(x=null){....} 若想让自己的代码更简洁,可以这样写: st ...

  5. asp.net mvc ViewBag常用操作

    1.视图获取json类型数据 var str = '@(ViewBag.loginInfoList)'; if ($.trim(str).length>0) { re = new RegExp( ...

  6. APS.NET MVC + EF (10)---使用AJAX

    在Web系统中,Ajax技术已经成为提高用户体验的必备技术.开发Ajax程序,涉及两方面的内容:一是客户端技术,二是服务器端技术. (1)客户端技术 核心工作是通过JavaScript向服务器发送数据 ...

  7. Delphi - 10进制16进制相互转换

    10进制转16进制 使用IntToHex可以实现十进制到十六进制的转换,注意这里的参数有两个,第一个表示需要被转换的10进制数,第二个表示转换后用几位来显示16进制数. 代码如下: function ...

  8. Delphi - Logs记录,函数实现MsgDsp

    Logs记录-函数实现MsgDsp 大多数时候,我们不太希望消息以交互的形式出现,这个时候我们可以在窗体上放置一个Memo,然后单独开一个线程进行监视,从而实现把消息实时的显示出来,便于开发者分析. ...

  9. centos7 配置nginx vim语法高亮

    看了Nginx核心知识100讲,按照他的做法,没有配置成功,可以使用下面的方法: 下载nginx源码,http://nginx.org/en/download.html 这里下载的是:nginx-1. ...

  10. 如何简单使用tensorboard展示(一)

    我使用tensorboard中的graph做了展示,至于其它功能可以类推,其代码如下: import numpy as npimport tensorflow as tf x_img = np.arr ...