T1 子集和

解题思路

大概是一个退背包的大白板,然而我考场上想复杂了,竟然还用到了组合数。

但是大概意思是一样的,有数的最小值一定是一个在 \(a\) 数组中存在的数字。

那么我们想办法除去它对应的贡献,可以一个一个退,也可以组合数一下一起退。。。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigend long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e4+10;
int n,cnt,m,top,s[N],t[N],ans[N],all[N];
inline int C(int x,int y)
{
__int128 temp=1; int p=1;
for(int i=y+1;i<=x;i++){temp*=i; while(p<=x-y&&temp%p==0) temp/=p,p++;}
return (int)temp;
}
#undef int
int main()
{
#define int long long
freopen("subset.in","r",stdin); freopen("subset.out","w",stdout);
n=read(); m=read();
for(int i=0;i<=m;i++) s[i]=read();
while(cnt<n)
{
int num,tot;
for(int i=1;i<=m;i++)
if(s[i]){num=i;tot=s[i];break;}
for(int i=1;i<=tot;i++) ans[++cnt]=num;
for(int i=1;i<=tot;i++) all[i]=C(tot,i),s[i*num]-=all[i];
for(int j=num+1;j<=m;j++)
{
if(!s[j]) continue;
for(int k=1;k<=tot;k++)
s[j+num*k]-=all[k]*s[j];
}
}
sort(ans+1,ans+n+1);
for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
return 0;
}

T2 异或

解题思路

官方题解是枚举 \(k\) 的位置,然后 Tire 树计算答案,然而。。。

我的做法是对于不同的二进制位枚举 \(j\) 的位置,并计算贡献。

假设当前处理到的二进制位是 \(p\) 所有二进制位最高是 \(m\) 。

那么一对 \((i,k)\) 对于 \(j\) 有贡献,当且仅当 \(a_i,a_k\) 的 \([p+1,m]\) 这几个二进制位相同,并且如果 \(a_j\) 的这一位是 0 \(a_i\) 的这一位也是 0 \(a_k\) 的这一位也是 1 ,或者\(a_j\) 的这一位是 1 \(a_i\) 的这一位也是 1 \(a_k\) 的这一位也是 0 。

那么我们可以对于 \([p+1,m]\) 这几位出现过的数字以及 \(p\) 位上的数字开一个桶,然后直接计算贡献。

然而这样需要优化,我们可以计算每一次移动的变化值,然后记录第 \(j\) 个数字的第 \(p\) 位是 1 或者 0 的答案直接计入贡献。

具体实现开一个前缀的桶一个后缀的桶,然后可能需要离散化一下,这样会被卡常。。。

发现有些比较高的位是没有必要离散化的我们直接算就可以了,于是卡常成功!!

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigend long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e7+10,M=5e5+10;
int n,m,ans,cnt,lsh[N],s[M],p[M],pre[2][N],suf[2][N];
inline void solve(register int pos)
{
register int U=((1ll<<m-pos)-1)<<pos+1,bas[2]={0,0};
if((1ll<<m-pos)-1<=10000000)
{
for(register int i=1;i<=n;i++) p[i]=(s[i]&U)>>pos+1;
for(register int i=1;i<=n;i++) pre[0][p[i]]=pre[1][p[i]]=suf[1][p[i]]=suf[0][p[i]]=0;
}
else
{
for(register int i=1;i<=n;i++) lsh[i]=(s[i]&U)>>pos+1;
sort(lsh+1,lsh+n+1); cnt=unique(lsh+1,lsh+n+1)-lsh-1;
for(register int i=1;i<=cnt;i++) pre[0][i]=pre[1][i]=suf[1][i]=suf[0][i]=0;
for(register int i=1;i<=n;i++) p[i]=lower_bound(lsh+1,lsh+cnt+1,(s[i]&U)>>pos+1)-lsh;
}
for(register int i=1;i<=n;i++) suf[(s[i]>>pos)&1][p[i]]++;
for(register int i=1;i<=n;i++)
{
register int p1=(s[i]>>pos)&1;
suf[p1][p[i]]--; bas[p1^1]-=pre[p1^1][p[i]];
ans+=bas[p1];
pre[p1][p[i]]++; bas[p1]+=suf[p1^1][p[i]];
}
}
#undef int
int main()
{
#define int long long
freopen("xor.in","r",stdin); freopen("xor.out","w",stdout);
n=read(); for(int i=1;i<=n;i++) s[i]=read(),m=max(m,(int)log2(s[i]));
for(int i=0;i<=m;i++) solve(i); printf("%lld",ans);
return 0;
}

T3 异或 2

解题思路

此题需要高精,于是我掌握了 string 高精。。。

直接推一波柿子,其实还是比较好理解的。。

直接记忆化搜索实现即可,最多递归 log 层复杂度完全可以接受。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigend long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
inline void write(string x){reverse(x.begin(),x.end());for(auto it:x)putchar(it+'0');putchar('\n');}
string n; map<string,string> f;
string mul(string x,int val)
{
int lim=x.size(); string y; for(auto it:x) y.push_back(it*val); y.push_back(0);
for(int i=0;i<y.size()-1;i++) y[i+1]+=y[i]/10,y[i]%=10; if(!(*(--y.end()))) y.pop_back();
return y;
}
string add(string x,string y)
{
int lim=max(x.size(),y.size())+1; string z; for(int i=1;i<=lim;i++) z.push_back(0);
for(int i=0;i<x.size();i++) z[i]+=x[i]; for(int i=0;i<y.size();i++) z[i]+=y[i];
for(int i=0;i<z.size()-1;i++) z[i+1]+=z[i]/10,z[i]%=10; if(!(*(--z.end()))) z.pop_back();
return z;
}
string div(string x)
{
for(int i=0;i<x.size();i++){if((x[i]&1)&&i) x[i-1]+=5; x[i]/=2;}
if(!(*(--x.end()))) x.pop_back();
return x;
}
string del(string x,int val)
{
if(x[0]>=val) return x[0]-=val,x;
int pos=0;
for(int i=1;i<x.size();i++)
if(x[i]){x[pos=i]--;break;}
for(int i=pos-1;i>=1;i--) x[i]=9;
x[0]=x[0]+10-val;
return x;
}
string dfs(string x)
{
if(f.find(x)!=f.end()) return f.find(x)->second;
string k=div(x),temp;
if(x[0]&1) temp=add(mul(dfs(k),4),mul(k,6));
else temp=add(add(mul(dfs(k),2),mul(dfs(del(k,1)),2)),del(mul(x,2),4));
return f.insert(make_pair(x,temp)),temp;
}
#undef int
int main()
{
#define int long long
freopen("rox.in","r",stdin); freopen("rox.out","w",stdout);
cin>>n; reverse(n.begin(),n.end());
for(int i=0;i<n.size();i++) n[i]-='0';
string t1,t2; t1.clear(); t2.clear(); t1.push_back(0); t2.push_back(0); f.insert(make_pair(t1,t2));
t1.clear(); t1.push_back(1); f.insert(make_pair(t1,t2)); t1.clear(); t1.push_back(2); f.insert(make_pair(t1,t2));
t1.clear(); t2.clear(); t1.push_back(3); t2.push_back(6); f.insert(make_pair(t1,t2));
write(dfs(n));
return 0;
}

T4 卡牌游戏

解题思路

又是老脸买原题系列(虽然我没做过)

发现其实是若干个联通块,一个合法的联通块显然只能是基环树或者树,对于 -1 的情况直接并茶几根据点数边数关系判断即可。。

那么答案就是所有的联通块最小操作次数加和,方案数就是所有联通块的方案数乘积。

对于一个联通块而言,我们把牌的两边互相连边,边权分别赋值为 0 或者 1 ,那么最后的形态一定是一某个节点为根,所有的边都指向儿子。

对于树的情况直接换根 DP 即可,基环树的情况一定出现在环上的点为根的时候,直接统计。。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigend long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=2e5+10,INF=1e18,mod=998244353;
int n,ans1,ans2=1,cnt1,cnt2,id,banx,bany,du[N],fa[N],f[N],g[N];
int tot=1,head[N],ver[N<<1],nxt[N<<1],edge[N<<1];
bool vis[N];
pair<int,int> siz[N];
void add_edge(int x,int y,int val)
{
ver[++tot]=y; edge[tot]=val; du[y]++;
nxt[tot]=head[x]; head[x]=tot;
}
int find(int x)
{
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void dfs(int x,int fa)
{
vis[x]=true;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i]; if(to==fa) continue;
if(vis[to]){id=i;banx=x;bany=to;continue;}
dfs(to,x); f[x]+=edge[i]+f[to];
}
}
void dfs2(int x,int fa)
{
if(g[x]<cnt1) cnt1=g[x],cnt2=1;
else cnt2+=g[x]==cnt1;
for(int i=head[x];i;i=nxt[i])
if(ver[i]!=fa&&i!=id&&(i!=(id^1)))
if(edge[i]) g[ver[i]]=g[x]-1,dfs2(ver[i],x);
else g[ver[i]]=g[x]+1,dfs2(ver[i],x);
}
#undef int
int main()
{
#define int long long
freopen("card.in","r",stdin); freopen("card.out","w",stdout);
n=read();
for(int i=1,x,y;i<=n;i++)
x=read(),y=read(),
add_edge(x,y,1),add_edge(y,x,0);
for(int i=1;i<=2*n;i++) fa[i]=i,siz[i]=make_pair(1,du[i]);
for(int i=1;i<=n;i++)
{
int x=find(ver[i<<1]),y=find(ver[i<<1|1]); if(x==y) continue;
siz[y]=make_pair(siz[x].first+siz[y].first,siz[x].second+siz[y].second);
fa[x]=y;
}
for(int i=1;i<=2*n;i++)
if(find(i)==i&&siz[i].second>siz[i].first*2)
printf("-1 -1"),exit(0);
for(int i=1;i<=2*n;i++)
{
if(vis[i]) continue; cnt1=INF; cnt2=id=0;
dfs(i,0); g[i]=f[i]; dfs2(i,0);
if(!id){ans1+=cnt1;ans2=ans2*cnt2%mod;continue;}
int t1=g[banx]+(edge[id]^1),t2=g[bany]+edge[id];
ans1+=min(t1,t2); if(t1==t2) ans2=ans2*2%mod;
}
printf("%lld %lld",ans1,ans2);
return 0;
}

NOIP模拟96(多校29)的更多相关文章

  1. NOIP模拟83(多校16)

    前言 CSP之后第一次模拟赛,感觉考的一般. 不得不吐槽多校联测 OJ 上的评测机是真的慢... T1 树上的数 解题思路 感觉自己思维有些固化了,一看题目就感觉是线段树. 考完之后才想起来这玩意直接 ...

  2. NOIP模拟92(多校25)

    前言 所以说这次是 HZOI 多校联测巅峰????(题目,数据过水??) T1 石子合并 解题思路 签到题. 发现我们可以给每个数字附一个正负号,每个数字的贡献就是它本身乘上这个符号. 发现至少应该有 ...

  3. NOIP模拟84(多校17)

    T1 宝藏 解题思路 考场上一眼出 \(nlog^2\) 做法,然后没看见是 1s 3e5 的数据,我竟然以为自己切了?? 考完之后尝试着把二分改为指针的移动,然后就过了??或许是数据水吧,感觉自己的 ...

  4. NOIP模拟85(多校18)

    前言 好像每个题目背景所描述的人都是某部番里的角色,热切好像都挺惨的(情感上的惨). 然后我只知道 T1 的莓,确实挺惨... T1 莓良心 解题思路 首先答案只与 \(w\) 的和有关系,于是问题就 ...

  5. NOIP模拟86(多校19)

    T1 特殊字符串 解题思路 \(f_{i,j}\) 表示前 \(i\) 个字符中结尾为 \(j\) 的最大贡献. 转移枚举当前位置于之前位置结尾的组合加上贡献即可. 对于边界问题,容易发现选择 1 一 ...

  6. NOIP模拟88(多校21)

    前言 对于这套题的总体感觉就是难,然后就是自己很菜... 对于 T1 考试时只会一个最垃圾的背包,考完之后对于思路这一块也不是很顺利,大概这就是薄弱的地方吧. 然后 T2 是比较简单的一道题了,但是考 ...

  7. NOIP模拟99(多校31)

    T1 法阵 解题思路 原题3100,张口放 T1(出题人原话) 思维题,合法的情况其实就是上下两个梯形拼起来的样子. 他们的边界都是在 \(i\) 轴上面,但是不能相交. 于是我们可以尝试两者相交的纵 ...

  8. Nescafe #29 NOIP模拟赛

    Nescafe #29 NOIP模拟赛 不知道这种题发出来算不算侵权...毕竟有的题在$bz$上是权限题,但是在$vijos$似乎又有原题...如果这算是侵权的话请联系我,我会尽快删除,谢谢~ 今天开 ...

  9. noip模拟29[简单的板子题](虽然我不会)

    \(noip模拟29\;solutions\) 这次考试给我最大的伤害,让我意识到了差距 这场考试可以说是非常的简单,就是简单到,看两眼,打个表就有结果了 但是呢?我考得非常的完蛋,只有30pts 据 ...

随机推荐

  1. HTML 网页开发、CSS 基础语法——五. 编辑器

  2. bzoj#2407-探险【最短路,二进制分组】

    正题 题目链接:https://darkbzoj.tk/problem/2407 题目大意 \(n\)个点的一张无向图(但是正反权值不同),求一个从\(1\)出发回到\(1\)且不经过重复边的最短路径 ...

  3. CF1392G-Omkar and Pies【dp】

    正题 题目链接:https://www.luogu.com.cn/problem/CF1392G 题目大意 两个长度为\(k\)的起始和目标01串. \(n\)个操作交换起始串的两个位置,选择一段长度 ...

  4. Python列表操作常用API

    1.列表的概念 (1)列表的定义 列表是Python中一种基本的数据结构.列表存储的数据,我们称为元素.在列表中的每个元素都会有一个下标来与之对应,第一个索引是0,第二个索引是1,依此类推的整数. 列 ...

  5. HDFS 10 - HDFS 的联邦机制(Federation 机制)

    目录 1 - 为什么需要联邦 2 - Federation 架构设计 3 HDFS Federation 的不足 版权声明 1 - 为什么需要联邦 单 NameNode 的架构存在的问题:当集群中数据 ...

  6. 跳表--怎么让一个有序链表能够进行"二分"查找?

    对于一个有序数组,如果要查找其中的一个数,我们可以使用二分查找(Binary Search)算法,将它的时间复杂度降低为O(logn).那查找一个有序链表,有没有办法将其时间复杂度也降低为O(logn ...

  7. Docker小白到实战之Docker Compose在手,一键足矣

    前言 Docker可以将应用程序及环境很方便的以容器的形式启动,但当应用程序依赖的服务比较多,或是遇到一个大系统拆分的服务很多时,如果还一个一个的根据镜像启动容器,那就有点累人了,到这有很多小伙伴会说 ...

  8. Midway Serverless 发布 2.0,一体化让前端研发再次提效

    作者 | 张挺 来源 | Serverless 公众号 自去年 Midway Serverless 1.0 发布之后,许多业务开始尝试其中,并利用 Serverless 容器的弹性能力,减少了大量研发 ...

  9. 题解 CF914G Sum the Fibonacci

    题目传送门 题目大意 给出\(n,s_{1,2,...,n}\),定义一个五元组\((a,b,c,d,e)\)合法当且仅当: \[1\le a,b,c,d,e\le n \] \[(s_a\vee s ...

  10. Python - faker

    安装 faker pip install Faker pip install Dumper 设置生成器 from faker import Faker fake = Faker() 它可以通过访问以想 ...