B-Bitwise Exclusive-OR Sequence

牛客网

题意:对于\(n(n<=1*10^5)\)个数的序列,给定\(m(m<=2*10^5)\)个限制条件,每个限制条件形如\(u\) \(v\) \(w(w<2^{30})\)表示\(a_u\) \(xor\) \(a_v\) \(=\) \(w\),构造一个满足所有限制条件序列,使得序列所有元素的和最小;如果不存在输出-1。

分析:根据按位异或的性质,把w拆成30位的二进制数来考虑。对于一组限制条件u v w,如果w的第k位为1,那么\(a_u\)和\(a_v\)的第k位不同;如果w的第k位为1,那么\(a_u\)和\(a_v\)的第k位相同。可以用扩展域并查集来表示上述两种情况,设\(f[i]\)表示\(a_i\)的第k位为1,\(f[i+n]\)表示\(a_i\)的第k位为0,那么如果\(a_u\)和\(a_v\)的第k位不同,则合并\(f[u]\)和\(f[v+n]\),\(f[u+n]\)和\(f[v]\);如果\(a_u\)和\(a_v\)的第k位相同,则合并\(f[u]\)和\(f[v]\),\(f[u+n]\)和\(f[v+n]\)。序列不存在等价于并查集合并过程中出现矛盾。每个限制条件都处理完之后,对于当前第\(k\)位,其对答案产生的贡献就是赋值为1的个数\(num\)乘上\(2^k\)。

为了求\(num\),对于每个集合还要记录一个size,初始化\(size[i]=1(1<=i<=n),size[i]=0(n+1<=i<=n+n)\),因为任意\(a_i\)第k位要么为0要么为1,只可能出现一种情况,而且最后\(i\)和\(i+n\)一定在两个相对的集合,此时这两个集合赋1赋0是可以相互交换的(二分图染色的性质),因此不妨开始先认为第k位都为1。于是\(num=min(size[find(i)],size[find(i+n)])\),即相对的两个集合谁\(size\)小就对谁赋1,保证最优。

时间复杂度\(O(30*mlogn)\),可能会卡常?据说优化的二分图做法可以做到\(mlogn\)。最后提醒一句,十年oi一场空......

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
char ch = getchar(); int x = 0, f = 1;
while (ch < '0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
while ('0' <= ch && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
const int N=2e5+5;
int n,m;
int u[N],v[N],w[N],fa[N],Size[N];
int find(int x){
if(x!=fa[x])fa[x]=find(fa[x]);
return fa[x];
}
int main(){
n=read();m=read();
for(int i=1;i<=m;++i){
u[i]=read();v[i]=read();w[i]=read();
}
ll ans=0;
for(int k=0;k<30;++k){
for(int i=1;i<=n;++i){
fa[i]=i;fa[i+n]=i+n;
Size[i]=1;Size[i+n]=0;
}
for(int i=1;i<=m;++i){
int x=find(u[i]),y=find(v[i]);
int xx=find(u[i]+n),yy=find(v[i]+n);
if(w[i]&(1<<k)){//异或为1
if(x==y){
cout<<"-1"<<endl;
return 0;
}
if(x==yy)continue;
fa[x]=yy;Size[yy]+=Size[x];
fa[xx]=y;Size[y]+=Size[xx];
}
else{
if(x==yy){
cout<<"-1"<<endl;
return 0;
}
if(x==y)continue;
fa[x]=y;Size[y]+=Size[x];
fa[xx]=yy;Size[yy]+=Size[xx];
}
}
for(int i=1;i<=n;++i){
int x=find(i),y=find(i+n);
ans+=1ll*min(Size[x],Size[y])*(1ll<<k);
Size[x]=0;Size[y]=0;
}
}
cout<<ans<<endl;
return 0;
}

E-Edward Gaming, the Champion

牛客网

题意:给定一个小写字母字符串(长度\(<=200000\)),查找"edgnb"出现的次数。

分析:签到题。略。

F-Encoded Strings I

牛客网

题意:给定一个长度为\(n(n<=1000)\)的字符串S,对于每一个前缀子串\(S[1]~S[i](1<=i<=n)\),都可以转换为另一个字符串\(T_i\),转换规则为:对于某个字符\(c\),在子串中它最后出现在位置\(j(1<=j<=i)\),如果位置\(j\)后面还有\(k\)个与\(c\)不同的字符,那么前缀子串中的所有字符\(c\)都要转换成\(a\)~\(z\)中的第k+1个字符。对于所有的\(T_i\)输出字典序最大的那一个。

分析:\(n<=1000\),可以直接根据题意暴力来做。转换规则看上去很复杂,实际上就是对于每一个前缀子串从后往前扫描,第一个碰到的新字符转换为\('a'\),第二个碰到的新字符转换为\('b'\),依此类推即可。每一次转换出新串之后,和当前的最优解比较字典序看是否需要更新即可。时间复杂度\(O(n^2)\)。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
char ch = getchar(); int x = 0, f = 1;
while (ch < '0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
while ('0' <= ch && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
const int N=1e3+5;
char s[N],ch[N],Ans[N],cnt[N];
int tong[25],ans;
void update(char s1[N],int len1,char s2[N],int len2){
for(int i=1;i<=min(len1,len2);++i){
if(s1[i]>s2[i])return;
if(s1[i]<s2[i]){
ans=len2;
for(int j=1;j<=len2;++j)Ans[j]=s2[j];
return;
}
}
if(len1>=len2)return;
ans=len2;
for(int j=1;j<=len2;++j)Ans[j]=s2[j];
}
int main(){
int n=read();
for(int i=1;i<=n;++i)cin>>s[i];
ans=1;Ans[1]='a';
for(int i=2;i<=n;++i){
for(int j=1;j<=20;++j)tong[j]=0;
int now=0;
for(int j=i;j>=1;--j){
if(tong[s[j]-'a'+1]==0)tong[s[j]-'a'+1]=++now;
cnt[j]=tong[s[j]-'a'+1]-1+'a';
}
update(Ans,ans,cnt,i);
}
for(int i=1;i<=ans;++i)cout<<Ans[i];cout<<endl;
return 0;
}

H-Line Graph Matching

牛客网

题意:给定n个点m条边的无向图,相邻的两条边可以匹配,求所有可以匹配的边的最大边权和。\(n<=10^5,n-1<=m<=2*10^5\)。保证无向图一定联通,且不存在自环和重边。

分析:m为偶数,那么所有边都能匹配,直接输出所有边权之和即可。m为奇数,考虑删一条边权最小的边,这条边可删,要么是无向图的非割边(删掉这条边无向图仍然连通,因此剩下的边仍然可以两两匹配),要么是无向图的割边且该割边把无向图分成两个连通块之后,每个连通块内边的条数均为偶数。

求无向图的割边使用tarjan算法(模板),求出所有的割边之后,可以像有向图那样缩点,其实割边一定不在环内,非割边一定在环内,缩点之后整个无向图成了一棵树,每条树边就是原图的割边。对树进行dfs,每次遍历到一条边,就判断删除这条边之后,两个连通块内是否都是偶数条边,因此时间复杂度为\(O(n)\)。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
char ch = getchar(); int x = 0, f = 1;
while (ch < '0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
while ('0' <= ch && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
const int N = 1e5 + 5;
const int M = 4e5 + 5;
int n, m,minn=1e9,du[N], vis[N],tot_du[N];
int tot = 1, head[N], nxt[M],to[M],w[M];
int cnt, dfn[N], low[N], bridge[M];
int TOT, HEAD[N], TO[M], NXT[M],W[M];
int dcc,col[N];
void add(int x, int y, int z) {
nxt[++tot] = head[x]; head[x] = tot; to[tot] = y;w[tot]=z;
}
void Add(int X, int Y,int Z) {
NXT[++TOT] = HEAD[X]; HEAD[X] = TOT; TO[TOT] = Y;W[TOT]=Z;
}
void tarjan(int u, int inedge) {
dfn[u] = low[u] = ++cnt;
int flag = 0;
for (int i = head[u]; i; i = nxt[i]) {
int v = to[i];
if (!dfn[v]) {
tarjan(v, i);
low[u] = min(low[u], low[v]);
if (low[v] > dfn[u])bridge[i] = bridge[i ^ 1] = 1;
}
else if (i != (inedge ^ 1))
low[u] = min(low[u], dfn[v]);
}
}
void dfs(int u){
col[u]=dcc;
tot_du[dcc]+=du[u];
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(col[v]||bridge[i])continue;
dfs(v);
}
}
void DFS1(int u){
for(int i=HEAD[u];i;i=NXT[i]){
int v=TO[i];
if(vis[v])continue;
vis[v]=1;
DFS1(v);
tot_du[u]+=tot_du[v];
}
}
void DFS2(int u){
for(int i=HEAD[u];i;i=NXT[i]){
int v=TO[i];
if(vis[v])continue;
vis[v]=1;
int pd=(tot_du[v]-1)/2;
if(pd%2==0){
minn=min(minn,W[i]);
}
DFS2(v);
}
}
int main() {
n = read(); m = read();
ll sum = 0;
for (int i = 1; i <= m; ++i) {
int x = read(), y = read(), z = read();
add(x, y, z); add(y, x, z);
++du[x]; ++du[y]; sum += z;
}
if (m % 2 == 0) {
cout << sum << endl;
return 0;
}
for (int i = 1; i <= n; ++i) {
if (!dfn[i])tarjan(i, 0);
}
//非割边直接删除
//割边如果分成偶数 就可以删除
for(int i=1;i<=n;++i){
if(!col[i]){
++dcc;
dfs(i);
}
}
for(int i=2;i<tot;i+=2){
if (!bridge[i]) {
minn=min(minn,w[i]);
continue;
}
int u=to[i];
int v=to[i^1];
if(col[u]==col[v])continue;
Add(col[u],col[v],w[i]);
Add(col[v],col[u],w[i]);
}
for(int i=1;i<=dcc;++i)vis[i]=0;
vis[1]=1;DFS1(1);
for(int i=1;i<=dcc;++i)vis[i]=0;
vis[1]=1;DFS2(1);
cout<<sum-minn<<endl;
return 0;
}

J-Luggage Lock

牛客网

题意:4位数字密码锁,给定初始状态\(a_1a_2a_3aa_4\)和最终状态\(b_1b_2b_3b_4\),每一次操作可以对连续的数位加一或者减一,求最少操作次数。多组询问,\(T<=1*10^5\)。

分析:初始状态和最终状态的具体数值不重要,重要的是每一位的差值,因此每一对询问都可以转换成\(0000\)到某一状态。具体来说,拿\(a_i-b_i\),如果为负数就加10。密码锁总共只有10000个状态,每一次操作可以扩展出20个状态,因此可以考虑BFS预处理出\(0000\)到所有状态的最短路,就能做到\(O(1)\)回答。

有一个点没注意,调了一个多小时,就是状态扩展的时候不能直接对密码锁数字整体操作,也就是不能用加\(1111\),加\(1110\),减\(110\)这些操作来代替,而要一位一位加减,同时记得加10模10.举个很简单的例子,现在的状态是\(1001\),如果直接减\(1111\),(再加10000)得到的是\(9990\),而实际上应该是\(990\)。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
char ch = getchar(); int x = 0, f = 1;
while (ch < '0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
while ('0' <= ch && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
const int N=2e5+5;
int q[10005],step[10005];
int dx[10][4]={{1,1,1,1},{1,1,1,0},{0,1,1,1},{1,1,0,0},{0,1,1,0},{0,0,1,1},{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}};
int main(){
int head=0,tail=0,st=0;
q[++tail]=st;step[st]=1;
while(head<tail){
int now=q[++head];
int a=now/1000,b=(now/100)%10,c=(now/10)%10,d=now%10;
for(int i=0;i<10;++i){
int aa=(a+dx[i][0])%10,bb=(b+dx[i][1])%10,cc=(c+dx[i][2])%10,dd=(d+dx[i][3])%10;
int x=aa*1000+bb*100+cc*10+dd;
if(!step[x]){
step[x]=step[now]+1;
q[++tail]=x;
} aa=(a-dx[i][0]+10)%10,bb=(b-dx[i][1]+10)%10,cc=(c-dx[i][2]+10)%10,dd=(d-dx[i][3]+10)%10;
x=aa*1000+bb*100+cc*10+dd;
if(!step[x]){
step[x]=step[now]+1;
q[++tail]=x;
}
}
}
int T=read();
while(T--){
string s1,s2;cin>>s1>>s2;
int now=0;
for(int i=0;i<4;++i){
now=now*10+((s1[i]-s2[i]+10)%10);
}
cout<<step[now]-1<<endl;
}
return 0;
}

L-Perfect Matchings

牛客网

题意:对于\(2*n\)个点的完全图,从图中删去包含这\(2*n\)个点的一棵树,即删去\(2n-1\)条边,求剩余图中完全匹配的个数。所谓一组完全匹配,即选出n条边使得这n条边恰好包含了所有\(2*n\)个顶点。\(n<=2000\)。

分析:剩余图中仍有\((n-1)*(2n-1)\)条边,数据量极其庞大。从删去的这棵树上面考虑,将删去的\(2n-1\)条边称为树边,拿完全图的总匹配个数减去包含了树边的匹配个数。如何求包含了树边的匹配方案数量,见下方链接。

题解

补充几个点:一、\(f[i][j][0/1]\)准确表达应该是以i为根的子树内,选了j条树边作为匹配边,此时点i没有/已经被匹配的方案数。二、当选出了i条树边,即匹配了\(2*i\)个点,还剩下\(2*(n-i)\)个点没有被匹配时,这\(2*(n-i)\)个点可以任意两两匹配产生的方案数为\(y=(2m-1)!!\),其中\(m=n-i\),这个式子的意思是\(2m-1\)内所有奇数的累积,因为现在有\(2m\)个点,对于任意第一个点它有\(2m-1\)种选法,去掉这两个点还剩\(2m-2\)个点,那么对于第三个点,它有\(2m-3\)种选法,因此产生的方案数为\(2m-1\)内所有奇数的累乘。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
char ch = getchar(); int x = 0, f = 1;
while (ch < '0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
while ('0' <= ch && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
const int N=4e3+5;
const int M=8e3+5;
const int mod=998244353;
int n,m,sz[N],jc[N];
ll tmp[N][2],f[N][N][2];
int tot,head[N],nxt[M],to[M];
void add(int a,int b){
nxt[++tot]=head[a];head[a]=tot;to[tot]=b;
}
void dfs(int u,int fa){
sz[u]=1;f[u][0][0]=1;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v==fa)continue;
dfs(v,u);
memset(tmp,0,sizeof(tmp));
for(int j=0;j<=sz[u]/2;++j){
for(int k=0;k<=sz[v]/2;++k){
tmp[j+k][0]=(tmp[j+k][0]+(1ll*f[u][j][0]*(f[v][k][0]+f[v][k][1])%mod))%mod;
tmp[j+k][1]=(tmp[j+k][1]+(1ll*f[u][j][1]*(f[v][k][0]+f[v][k][1])%mod))%mod;
tmp[j+k+1][1]=(tmp[j+k+1][1]+(1ll*f[u][j][0]*f[v][k][0])%mod)%mod;
}
}
sz[u]+=sz[v];
for(int i=0;i<=sz[u]/2;++i){
f[u][i][0]=tmp[i][0];
f[u][i][1]=tmp[i][1];
} }
}
void init(){
jc[0]=1;
for(int i=1;i<=m;i++){
if(i%2==0)jc[i]=jc[i-1];
else jc[i]=(1ll*i*jc[i-1])%mod;
}
}
int main(){
n=read();m=2*n;init();
for(int i=1;i<m;++i){
int x=read(),y=read();
add(x,y);add(y,x);
}
dfs(1,0);
ll ans=0;
for(int i=0;i<=n;++i){
ll x=(f[1][i][0]+f[1][i][1])%mod;
ll y;
if(i<n)y=jc[2*(n-i)-1];
else y=1;
if(i%2==0)ans=(ans+(1ll*x*y)%mod)%mod;
else ans=(ans-(1ll*x*y)%mod+mod)%mod;
}
cout<<ans<<endl;
return 0;
}

ICPC2021 沈阳站的更多相关文章

  1. 2016ACM/ICPC亚洲区沈阳站-重现赛赛题

    今天做的沈阳站重现赛,自己还是太水,只做出两道签到题,另外两道看懂题意了,但是也没能做出来. 1. Thickest Burger Time Limit: 2000/1000 MS (Java/Oth ...

  2. HDU 5950 Recursive sequence 【递推+矩阵快速幂】 (2016ACM/ICPC亚洲区沈阳站)

    Recursive sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Other ...

  3. HDU 5952 Counting Cliques 【DFS+剪枝】 (2016ACM/ICPC亚洲区沈阳站)

    Counting Cliques Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) ...

  4. HDU 5948 Thickest Burger 【模拟】 (2016ACM/ICPC亚洲区沈阳站)

    Thickest Burger Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)T ...

  5. HDU 5949 Relative atomic mass 【模拟】 (2016ACM/ICPC亚洲区沈阳站)

    Relative atomic mass Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Oth ...

  6. 2015ACM/ICPC亚洲区沈阳站 Pagodas

    Pagodas Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Sub ...

  7. 2015ACM/ICPC亚洲区沈阳站 B-Bazinga

    Bazinga Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Sub ...

  8. HDU 6227.Rabbits-规律 (2017ACM/ICPC亚洲区沈阳站-重现赛(感谢东北大学))

    Rabbits Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total S ...

  9. HDU 6225.Little Boxes-大数加法 (2017ACM/ICPC亚洲区沈阳站-重现赛(感谢东北大学))

    整理代码... Little Boxes Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/O ...

  10. ACM-ICPC 2016亚洲区域赛(沈阳站)游记(滚粗记)

    首发于QQ空间和知乎,我在这里也更一下.   前言 以前高中搞竞赛的时候,经常看到神犇出去比赛或者训练之后写游记什么的,感觉萌萌哒.但是由于太弱,就没什么心情好写.现在虽然还是很弱,但是抱着享受的心情 ...

随机推荐

  1. ASP判断一个字符是否为汉字的两种方法

    有的时候我们要求用户一定要输入汉字的信息,比如姓名和地址.那么,如何判断一个字符是不是汉字呢?其实在asp中至少有两种方法: 一.直接将某字符用asc转为ascii码,如果是英文,他应该是0-127的 ...

  2. Android ViewModel,LiveData 简要分析

    ViewModel: 负责为关联UI(activity/fragment)进行数据管理,业务逻辑处理.不直接持有view引用,不对UI进行访问调用操作 对外通过暴露Livedata方式响应处理结果 L ...

  3. 7. Light (对象)

    Light Mode:模式 Realtime: 实时的.就是当前光照效果是实时的,不包含烘焙效果(即使场景曾经烘焙过) Mixed: 混合的.就是既使用烘焙数据对静态对象(Lightmap stati ...

  4. 使用伪元素 before 叹号

    .tip { width: 400px; line-height: 150%; border-left-color: #f66; color: #666; padding: 12px 24px 12p ...

  5. PHP中的超级变量

    超级变量,又名超级全局变量,是PHP内置的变量,这些变量在代码的任意位置都能正常使用 PHP中的超级变量 9种超级变量 $GLOBALS $_SERVER 9种超级变量 目前,PHP提供了9种超级变量 ...

  6. 中间件Middleware 使用及相关概念

    中间件是装配再应用管道里处理请求和相应的软件,是使用Use,Map,Run扩展方法配置的请求委托,请求委托可以是类,或者匿名方法.每个中间件可调用下一个中间件,直到终端中间件(不调用下一个中间件的就是 ...

  7. JAVA的注释和变量名称

    1.注释 (1)单行注释为//..... 例://这是一行注释 (2)多行注释是/*.......... .............*/ 例:/*我是第一行注释 我是第二行注释 我是第三行注释*/ ( ...

  8. 苹果App 上架 app store 提示 “构建版本错误”使用Application Loader发布App

    步骤1 打开Application Loader(有2种方法) 或 步骤2 使用开发者帐号登录 步骤3 选择需要上传发布的ipa包 选择成功后,会显示ipa包的相关信息 步骤4 上传验证 上传成功 转 ...

  9. JAVA笔记_方法递归调用

    方法递归调用   简单地说递归调用就是方法自己调用自己,每次调用时传入不同的变量,递归有助于编程者解决复杂问题,同时可以让代码变得更加简洁. 递归调用执行机制案例1: /** * @ClassName ...

  10. GET请求数据量大造成的问题

    在实际的开发过程中,我们偶尔或者遇到过要导出列表中所有的数据.假设列表中有十万条数据,那么导出所有,意味着要大批量的走查询接口,通常我们的后台的API接口GET请求支持的查询长度不得大于1000,(比 ...