Contest Page

开题开错翻车场.jpg

A

sol

$A > \frac{W}{2}$或者$B > \frac{H}{2}$的时候无解,否则构造方法长下面这样



#include<bits/stdc++.h>
using namespace std; int main(){
int H , W , A , B; cin >> H >> W >> A >> B;
if(A > W / 2 || B > H / 2){puts("-1"); return 0;}
for(int i = 1 ; i <= B ; ++i){
for(int j = 1 ; j <= A ; ++j)
putchar('0');
for(int j = A + 1 ; j <= W ; ++j)
putchar('1');
putchar('\n');
}
for(int i = B + 1 ; i <= H ; ++i){
for(int j = 1 ; j <= A ; ++j)
putchar('1');
for(int j = A + 1 ; j <= W ; ++j)
putchar('0');
putchar('\n');
}
return 0;
}

B

sol

我们令$i,j$相等当对$[i,i+K)$排序和对$[j,j+K)$排序后结果一样。我们考虑这样会有什么性质。


当这两个排序区间无交的时候,只有可能这两个区间都是升序的,否则不可能会相等;


如果两个排序区间有交,则一定满足$[j,i+K)$中的最小值大于$[i,j)$中的最大值,且$[i,j)$的元素升序排列;$[j,i+K)$中的最大值小于$[i+K,j+K)$中的最小值,且$[i+K,j+K)$的元素升序排列。那么不难得到$\forall x,y \in [i,j]$,$x$和$y$都是相等的,也就是原序列一定可以划分成若干段极大区间满足区间内任意两个位置相等。


用单调队列/set把这些区间找出来,然后把第二段中的情况稍加判断即可。


#include<bits/stdc++.h>
using namespace std; int main(){
static int arr[200003]; int N , K;
cin >> N >> K; for(int i = 1 ; i <= N ; ++i) cin >> arr[i];
set < int > num; int ans = 1;
for(int i = 1 ; i <= K ; ++i) num.insert(arr[i]);
for(int i = K + 1 ; i <= N ; ++i){
ans += *num.begin() != arr[i - K] || *--num.end() > arr[i];
num.erase(arr[i - K]); num.insert(arr[i]);
}
bool flg = 0; int pre = -1 , cnt = 0;
for(int i = 1 ; i <= N ; ++i){
if(pre < arr[i]) ++cnt;
else cnt = 1;
pre = arr[i];
if(cnt == K){flg = 1; --ans;}
}
cout << ans + flg; return 0;
}

C

sol

$\begin{align*} \sum\limits_{i=1}^N \sum\limits_{j=i+1}^N lcm(A_i,A_j) & = \sum\limits_{i=1}^N \sum\limits_{j=i+1}^N \frac{A_iA_j}{gcd(A_i,A_j)} \\ & = \sum\limits_{d = 1}^{maxA} \frac{1}{d} \sum\limits_{d|A_i} \sum\limits_{d|A_j} A_iA_j[gcd(A_i,A_j)=d] \\ &= \sum\limits_{d = 1}^{maxA} \frac{1}{d} \sum\limits_{d|A_i} \sum\limits_{d|A_j} A_iA_j \sum\limits_{p | \frac{A_i}{d} , p | \frac{A_j}{d}} \mu(p) \\ &= \sum\limits_{p=1}^{maxA} \mu(p) \sum\limits_{d=1}^{\frac{maxA}{p}} \frac{1}{d} \sum\limits_{dp|A_i} \sum\limits_{dp|A_j} A_iA_j \\ &= \sum\limits_{T=1}^{maxA} \sum\limits_{p | T} \mu(p) \frac{p}{T} \sum\limits_{T|A_i} \sum\limits_{T|A_j} A_iA_j \end{align*}$


先对于每个$T$预处理$\sum\limits_{p | T} \mu(p) \frac{p}{T}$,然后注意到$\sum\limits_{T|A_i} \sum\limits_{T|A_j} A_iA_j = \frac{(\sum\limits_{T | A_i} A_i)^2 - \sum\limits_{T | A_i}A_i^2}{2}$,对于每一个$T$枚举倍数算出$\sum\limits_{T | A_i} A_i$和$\sum\limits_{T | A_i}A_i^2$即可。


复杂度可以做到$O(maxA\ log\ maxA)$,但是很呆的我后面一部分写的是枚举约数……

#include<bits/stdc++.h>
using namespace std; #define ll long long
const ll _ = 1e6 + 7 , MOD = 998244353 , INV2 = (MOD + 1) / 2;
int N , inv[_] , prm[_] , mu[_] , sum[_] , cnt; bool nprm[_];
int sum1[_] , sum2[_]; vector < int > ys[_]; void init(){
mu[1] = 1;
for(int i = 2 ; i <= 1e6 ; ++i){
if(!nprm[i]){prm[++cnt] = i; mu[i] = -1;}
for(int j = 1 ; prm[j] * i <= 1e6 ; ++j){
nprm[prm[j] * i] = 1;
if(i % prm[j] == 0) break;
mu[i * prm[j]] = -mu[i];
}
}
inv[1] = 1;
for(int i = 2 ; i <= 1e6 ; ++i)
inv[i] = MOD - 1ll * (MOD / i) * inv[MOD % i] % MOD;
for(int i = 1 ; i <= 1e6 ; ++i)
for(int j = i ; j <= 1e6 ; j += i){
sum[j] = (sum[j] + 1ll * mu[i] * i + MOD) % MOD;
ys[j].push_back(i);
}
} signed main(){
init(); ios::sync_with_stdio(0);
for(cin >> N ; N ; --N){
int p; cin >> p;
for(auto t : ys[p]){
sum1[t] = (sum1[t] + p) % MOD;
sum2[t] = (sum2[t] + 1ll * p * p) % MOD;
}
}
int ans = 0;
for(int i = 1 ; i <= 1e6 ; ++i)
ans = (ans + 1ll * sum[i] * inv[i] % MOD * ((1ll * sum1[i] * sum1[i] - sum2[i] + MOD) % MOD * INV2 % MOD) % MOD) % MOD;
cout << ans;
return 0;
}

D

sol

先考虑$0$边。我们把答案图中的所有桥拿出来,那么$0$边的两个端点一定会在桥边形成的连通块上。用并查集并一下有哪些集合在同一个桥边连通块上。


那么对于$1$边来说,它们就不能在同一个桥边连通块上。这里判一下如果不合法输出No。


开这道题的时候盲猜$(A,B,1)+(B,C,1) \rightarrow (A,C,1)$然后就凉凉了


接下来考虑最小化和最大化边数方案。最小化边数的方案要么是树(就是没有$1$限制),要么就是一个基环树。最大化边数的方案就是所有桥边的连通块中选择一个点,然后这些点连完全图。

#include<bits/stdc++.h>
using namespace std; #define ll long long
#define PII pair < int , int >
const int _ = 1e5 + 7;
ll fa[_] , sz[_] , N , M , Q;
vector < PII > to0 , to1; int find(int x){return fa[x] == x ? x : (fa[x] = find(fa[x]));}
void merge(int p , int q){if((p = find(p)) == (q = find(q))) return; fa[p] = q; sz[q] += sz[p];} signed main(){
ios::sync_with_stdio(0);
cin >> N >> M >> Q;
for(int i = 1 ; i <= N ; ++i) sz[fa[i] = i] = 1;
for(int i = 1 ; i <= Q ; ++i){
int A , B , C; cin >> A >> B >> C; ++A; ++B;
if(C == 0) to0.push_back(PII(A , B));
else to1.push_back(PII(A , B));
}
for(auto t : to0) merge(t.first , t.second);
for(auto t : to1) if(find(t.first) == find(t.second)){puts("No"); return 0;}
ll mn = 0 , mx = 0 , cnt = 0; bool flg = 1;
for(int i = 1 ; i <= N ; ++i) if(find(i) == i){++cnt; mn += sz[i] - 1; mx += sz[i] - 1;}
for(int i = 1 ; i <= N ; ++i) if(fa[i] == i) sz[i] = 1; else sz[i] = 0;
for(auto t : to1) merge(t.first , t.second);
for(int i = 1 ; i <= N ; ++i)
if(fa[i] == i && sz[i] == 2 && cnt == 2){puts("No"); return 0;}
else if(sz[i] >= 2) flg = 0;
mn += cnt - flg; if(M < mn){puts("No"); return 0;}
mx += cnt * (cnt - 1) / 2; if(M > mx){puts("No"); return 0;}
puts("Yes"); return 0;
}

E

sol

要算最大值期望,考虑Min-Max容斥,也就是枚举每一个子集,计算其中存在一个满足要求时步数期望。不难发现这等价于方案经过的不存在任何一个位置满足要求的局面个数的期望。根据期望的线性性这等价于每一种不存在任何一个满足要求的局面经过次数的期望之和


设枚举的子集下标是$p_1,p_2,...,p_k$,它们的$A$之和是$S_0$,我们枚举的局面中第$p_i$个位置随机到了$x_i$次。注意到在第一次到达当前局面之后停留在该局面的期望次数是$\sum\limits_{i=0}^\infty \left(\frac{S - S_0}{S}\right)^i = \frac{S}{S_0}$,所以我们只需要计算到达这个状态的概率。


显然在我们现在所要解决的问题下我们可以忽略没有随机在下标$p_1,p_2,...,p_k$内的操作。那么我们在操作序列中,$p_i$随到了$x_i$次,随到这个位置的概率是$\frac{A_{p_i}}{S_0}$,根据多重组合可以得到概率是$(\sum x_i)! \prod \frac{\left(\frac{A_{p_i}}{S_0}\right)^{x_i}}{x_i!}$


对于一个集合我们现在要求出所有$x_i$序列的期望之和。考虑DP:设$f_{i,j}$表示考虑了前$i$个物品,$\sum x_i = j$时的所有可能的序列的$\prod \frac{\left(\frac{A_{p_i}}{S_0}\right)^{x_i}}{x_i!}$之和,转移考虑下一个位置的$x_i$是多少。这个的复杂度是$O((\sum B_{p_i})^2)$的。


最后我们还要求出对于所有的序列$p_i$的方案数之和,稍微转换上面的DP:设$f_{i,j,k}$表示考虑了前$i$个位置,对于所有可能的$p$和$x$中满足$\sum A_{p_i} = j , \sum x_i = k$的方案中$\prod \frac{A_{p_i}^{x_i}}{x_i!}$的和,转移考虑当前物品是否加入$p$序列、加入多少个。不难证明复杂度是$O(\sum A_i (\sum B_i)^2)$的。

#include<bits/stdc++.h>
using namespace std; const int MOD = 998244353;
int dp[403][403] , A[403] , B[403] , jc[403] , inv[403] , N , S; int poww(long long a , int b){
int times = 1;
while(b){
if(b & 1) times = times * a % MOD;
a = a * a % MOD; b >>= 1;
}
return times;
} int main(){
cin >> N; for(int i = 1 ; i <= N ; ++i){cin >> A[i] >> B[i]; S += A[i];}
dp[0][0] = MOD - 1; jc[0] = 1;
for(int i = 1 ; i <= 400 ; ++i) jc[i] = 1ll * jc[i - 1] * i % MOD;
inv[400] = poww(jc[400] , MOD - 2);
for(int i = 399 ; i >= 0 ; --i) inv[i] = inv[i + 1] * (i + 1ll) % MOD;
for(int i = 1 ; i <= N ; ++i)
for(int j = S ; j >= 0 ; --j)
for(int k = 400 ; k >= 0 ; --k)
if(dp[j][k])
for(int l = 0 , tms = 1 ; l < B[i] ; ++l , tms = 1ll * tms * A[i] % MOD)
dp[j + A[i]][k + l] = (dp[j + A[i]][k + l] - 1ll * tms * inv[l] % MOD * dp[j][k] % MOD + MOD) % MOD;
int ans = 0;
for(int i = 1 ; i <= S ; ++i){
int val = poww(i , MOD - 2) , val1 = i == S ? 1 : 1ll * S * poww(i , MOD - 2) % MOD;
for(int j = 0 ; j <= 400 ; ++j)
ans = (ans + 1ll * dp[i][j] * jc[j] % MOD * poww(val , j) % MOD * val1) % MOD;
}
cout << ans; return 0;
}

F

sol

最小鸽模板题因为$10^5$不敢开……


考虑计算排列中最少的相同数字的数量。


考虑对于排列$P$连有向边$(i,P_i)$然后找到所有的环。显然对于排列$A$来说每一个环要么不转,要么沿着环转一格。对于$Q$和$B$也是一致的。


给$P,Q$中的每个环一个标号,设$fP_i$表示$P$中第$i$个环有没有转,$fQ_i$表示$Q$中第$i$个环有没有转。接下来考虑每一个位置$i$,设它在$P$的第$x$个环上、$Q$的第$y$个环上。考虑以下情况对位置相同值相同的数量的贡献:


1.如果$P_i = i , Q_i = i$,无论如何这里都会有$1$的贡献;


2.如果$P_i = i , Q_i \neq i$那么会在$fQ_y = 0$的时候产生$1$的贡献;


3.如果$P_i \neq i , Q_i = i$,那么会在$fP_x = 0$的时候产生$1$的贡献;


4.如果$P_i \neq i , Q_i \neq i$时,当$fP_x = 0 , fQ_y = 0$时会产生$1$的贡献;


5.在4的基础上如果$P_i = Q_i$,则在$fP_x = 1 , fQ_y = 1$时也会产生$1$的贡献。


看起来十分最小鸽,但是45的限制都是$fP_x$和$fQ_y$同时相等的时候才会有贡献,这个似乎不太会做。那么我们把$fQ_y$的意义反转一下,$fQ_y = 0$表示$y$环转了,这样就是两个位置不相同就会产生贡献,就是一个经典的最小鸽问题了。


所以为什么这个的复杂度还是$O(n^{1.5})$的啊QAQ

#include<bits/stdc++.h>
using namespace std; namespace flow{
const int _ = 1e6 + 7 , __ = 1e7 + 7;
struct Edge{int end , upEd , f;}Ed[__];
int head[_] , dep[_] , cur[_] , cntEd = 1 , S , T; void addEd(int a , int b , int c){Ed[++cntEd] = (Edge){b , head[a] , c}; head[a] = cntEd;}
void addE(int a , int b , int c){addEd(a , b , c); addEd(b , a , 0);} queue < int > q;
bool bfs(){
memset(dep , 0 , sizeof(int) * (T + 1)); dep[S] = 1;
while(!q.empty()) q.pop(); q.push(S);
while(!q.empty()){
int t = q.front(); q.pop();
for(int i = head[t] ; i ; i = Ed[i].upEd)
if(Ed[i].f && !dep[Ed[i].end]){
dep[Ed[i].end] = dep[t] + 1;
if(Ed[i].end == T){memcpy(cur , head , sizeof(int) * (T + 1)); return 1;}
q.push(Ed[i].end);
}
}
return 0;
} int dfs(int x , int mn){
if(x == T) return mn;
int sum = 0;
for(int &i = cur[x] ; i ; i = Ed[i].upEd)
if(Ed[i].f && dep[Ed[i].end] == dep[x] + 1){
int t = dfs(Ed[i].end , min(Ed[i].f , mn - sum));
sum += t; Ed[i].f -= t; Ed[i ^ 1].f += t;
if(mn == sum) break;
}
return sum;
} int Dinic(int s , int t){S = s; T = t; int sum = 0; while(bfs()) sum += dfs(s , 1e9); return sum;}
}using flow::addE;
const int _ = 1e5 + 7;
int N , P[_] , Q[_] , belp[_] , belq[_]; int work(int *now , int *bel){
int cnt = 0;
for(int i = 1 ; i <= N ; ++i)
if(!bel[i]){++cnt; int tmp = i; do{bel[i] = cnt; i = now[i];}while(i != tmp);}
return cnt;
} int main(){
ios::sync_with_stdio(0); cin >> N;
for(int i = 1 ; i <= N ; ++i){cin >> P[i]; ++P[i];}
for(int i = 1 ; i <= N ; ++i){cin >> Q[i]; ++Q[i];}
int szp = work(P , belp) , szq = work(Q , belq) , T = szp + szq + 1 , ans = 0;
for(int i = 1 ; i <= N ; ++i)
if(P[i] == i && Q[i] == i) ++ans;
else if(P[i] == i) addE(szp + belq[i] , T , 1);
else if(Q[i] == i) addE(0 , belp[i] , 1);
else{
addE(belq[i] + szp , belp[i] , 1);
if(P[i] == Q[i]) addE(belp[i] , belq[i] + szp , 1);
}
cout << N - (ans + flow::Dinic(0 , T)); return 0;
}

AGC038的更多相关文章

  1. AtCoder AGC038 C-LCMs 题解

    题目链接:https://agc038.contest.atcoder.jp/tasks/agc038_c?lang=en 题意:给定一个数组,求这个数组中所有数对的LCM之和. 分析:网上看到了很多 ...

  2. AtCoder从小白到大神的进阶攻略

    前言 现在全球最大的编程比赛记分网站非CodeForces和AtCoder莫属了,@ezoixx130大佬已经在去年介绍过CodeForces了(传送门),那么现在我们主要谈一下AtCoder. 简介 ...

  3. 【做题记录】AtCoder AGC做题记录

    做一下AtCoder的AGC锻炼一下思维吧 目前已做题数: 75 总共题数: 239 每一场比赛后面的字母是做完的题,括号里是写完题解的题 AGC001: ABCDEF (DEF) AGC002: A ...

  4. AtCoder AGC038F Two Permutations (网络流、最小割)

    题目链接 https://atcoder.jp/contests/agc038/tasks/agc038_f 题解 好题. 首先观察到一个性质,对于排列\(P\), 其所形成的每个轮换中的点\(A_i ...

  5. AtCoder AGC038D Unique Path (图论)

    题目链接 https://atcoder.jp/contests/agc038/tasks/agc038_d 题解 orz zjr神仙做法 考虑把所有\(C_i=0\)的提示的两点连边,那么连完之后的 ...

  6. agc38C LCMs

    https://atcoder.jp/contests/agc038/tasks/agc038_c 题意:给\(a_i\),求\(\sum_{i=1}^n\sum_{j=i+1}^nlcm(a_i,a ...

随机推荐

  1. iOS开发使用Xcode的一些小技巧

    1.打开iOS项目 如果你当前目录下既有project又有workspace,你可以在终端使用命令“xed.”自动打开workspace,如果只有project,它会自动打开project. 2.清理 ...

  2. 6 Linux用户和用户组管理

    Linux系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户都必须首相像系统管理员申请账号,然后以这个账号身份进入系统 每个用户账号都拥有一个唯一的用户名和各自的口令 用户在登陆时键入 ...

  3. ansible自动化运维02

    ansible清单管理 inventory文件通常用于定义要管理主机的认证信息,例如:ssh登录用户名,密码,以及key相关信息. 举个例子:定义清单组 注意:组名为pro,关键字段children表 ...

  4. Java实现Redis的消息订阅和发布

    1.  首先需要一个消息监听器类 package com.sogou.baike.testimport.testSubscribe; import redis.clients.jedis.JedisP ...

  5. AMD 锐龙三代系列台式机处理器(台积电7纳米工艺)

    AMD 锐龙 5 AMD 锐龙 5 3500X 处理器 6核心6线程,基准时钟频率3.6GHz,最大加速时钟频率4.1GHz,一级缓存384KB,总二级缓存3MB,三级缓存32MB,内存支持最高DDR ...

  6. java.util.ConcurrentModificationException异常;java.util.ConcurrentModificationException实战

    写代码遇到这个问题,很多博客文章都是在反复的强调理论,而没有对应的实例,所以这里从实例出发,后研究理论: 一.错误产生情况 1 .字符型 (1)添加 public static void main(S ...

  7. 浏览器bug html 底部

  8. 前端之CSS(上)

    CSS CSS 简介 ## CSS介绍 CSS(Cascading Style Sheet,层叠样式表)定义如何显示HTML元素. 当浏览器读到一个样式表,它就会按照这个样式表来对文档进行格式化(渲染 ...

  9. Hibernate框架学习3

    一对多|多对一 一对多 多对一 级联操作 结论: 简化操作.一定要用,save-update,不建议使用delete. 关系维护 在保存时.两方都会维护外键关系.关系维护两次,冗余了. 多余的维护关系 ...

  10. postfix发邮件失败,日志和postqueue -p提示Connection refused

    1. postfix服务未启动 2. /etc/postfix/main.cf文件中未设置inet_interfaces = all