NOIP模拟96(多校29)
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)的更多相关文章
- NOIP模拟83(多校16)
前言 CSP之后第一次模拟赛,感觉考的一般. 不得不吐槽多校联测 OJ 上的评测机是真的慢... T1 树上的数 解题思路 感觉自己思维有些固化了,一看题目就感觉是线段树. 考完之后才想起来这玩意直接 ...
- NOIP模拟92(多校25)
前言 所以说这次是 HZOI 多校联测巅峰????(题目,数据过水??) T1 石子合并 解题思路 签到题. 发现我们可以给每个数字附一个正负号,每个数字的贡献就是它本身乘上这个符号. 发现至少应该有 ...
- NOIP模拟84(多校17)
T1 宝藏 解题思路 考场上一眼出 \(nlog^2\) 做法,然后没看见是 1s 3e5 的数据,我竟然以为自己切了?? 考完之后尝试着把二分改为指针的移动,然后就过了??或许是数据水吧,感觉自己的 ...
- NOIP模拟85(多校18)
前言 好像每个题目背景所描述的人都是某部番里的角色,热切好像都挺惨的(情感上的惨). 然后我只知道 T1 的莓,确实挺惨... T1 莓良心 解题思路 首先答案只与 \(w\) 的和有关系,于是问题就 ...
- NOIP模拟86(多校19)
T1 特殊字符串 解题思路 \(f_{i,j}\) 表示前 \(i\) 个字符中结尾为 \(j\) 的最大贡献. 转移枚举当前位置于之前位置结尾的组合加上贡献即可. 对于边界问题,容易发现选择 1 一 ...
- NOIP模拟88(多校21)
前言 对于这套题的总体感觉就是难,然后就是自己很菜... 对于 T1 考试时只会一个最垃圾的背包,考完之后对于思路这一块也不是很顺利,大概这就是薄弱的地方吧. 然后 T2 是比较简单的一道题了,但是考 ...
- NOIP模拟99(多校31)
T1 法阵 解题思路 原题3100,张口放 T1(出题人原话) 思维题,合法的情况其实就是上下两个梯形拼起来的样子. 他们的边界都是在 \(i\) 轴上面,但是不能相交. 于是我们可以尝试两者相交的纵 ...
- Nescafe #29 NOIP模拟赛
Nescafe #29 NOIP模拟赛 不知道这种题发出来算不算侵权...毕竟有的题在$bz$上是权限题,但是在$vijos$似乎又有原题...如果这算是侵权的话请联系我,我会尽快删除,谢谢~ 今天开 ...
- noip模拟29[简单的板子题](虽然我不会)
\(noip模拟29\;solutions\) 这次考试给我最大的伤害,让我意识到了差距 这场考试可以说是非常的简单,就是简单到,看两眼,打个表就有结果了 但是呢?我考得非常的完蛋,只有30pts 据 ...
随机推荐
- P3643-[APIO2016]划艇【dp】
正题 题目链接:https://www.luogu.com.cn/problem/P3643 题目大意 求有多少个\(n\)个数的序列\(x\)满足,\(x_i\in \{0\}\cup[a_i,b_ ...
- ARC122C-Calculator【乱搞,构造】
正题 题目链接:https://atcoder.jp/contests/arc122/tasks/arc122_c 题目大意 一个数对开始是\((0,0)\),每次可以选择一个数加一或者让一个数加上另 ...
- P4831-Scarlet loves WenHuaKe【组合数学】
正题 题目链接:https://www.luogu.com.cn/problem/P4831 题目大意 \(n*m\)的网格上放置\(2n\)个炮,要求互不能攻击. 数据满足\(n\leq m\leq ...
- sqlite3 c++使用以及提高速率(一万条每秒左右)
参考来源: sqlite3的C语言使用(三):https://www.leavesongs.com/C/sqlite3_3.html sqlite插入和查询效率提高方法及测试结果: http://bl ...
- Polya 定理 学习笔记
群 群的定义 我们定义,对于一个集合 \(G\) 以及二元运算 \(\times\),如果满足以下四种性质,那我们就称 \((G,\times)\) 为一个群. 1. 封闭性 对于 \(a\in G, ...
- 洛谷2494 [SDOI2011]保密 (分数规划+最小割)
自闭一早上 分数规划竟然还能被卡精度 首先假设我们已经知道了到每个出入口的时间(代价) 那我们应该怎么算最小的和呢? 一个比较巧妙的想法是,由于题目规定的是二分图. 我们不妨通过最小割的形式. 表示这 ...
- C/C++入门级小游戏——开发备忘录
很多工科的学生在大一都有一门课程,叫C语言程序设计.大概就是装个IDE然后和一个黑乎乎的窗口打交道,期末到了考完试就结束了.然而很多人可能都有一个疑惑:C语言究竟能干什么?除开嵌入式单片机这些高大上的 ...
- TypeError: module() takes at most 2 arguments (3 given)
1. 错误提示 2. 代码 class Parent: """定义父类""" def __init__(self): print(" ...
- vue3.x异步组件
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块 vue2.x 曾经简单的异步组件 components: { AsyncComponent: () =& ...
- 深入浅出Java内存模型
面试官:我记得上一次已经问过了为什么要有Java内存模型 面试官:我记得你的最终答案是:Java为了屏蔽硬件和操作系统访问内存的各种差异,提出了「Java内存模型」的规范,保证了Java程序在各种平台 ...