2021.11.13考试总结[冲刺NOIP模拟29]
T1 子集和
最小的元素可以直接确定下来。接下来不断做背包删去这个元素,递归进行即可。
\(code:\)
T1
#include<bits/stdc++.h>
using namespace std;
namespace IO{
#define int long long
char Ch[50];
int read(){
int x=0,f=1; char Ch=getchar();
while(Ch<'0'||Ch>'9'){ if(Ch=='-') f=-1; Ch=getchar(); }
while(Ch>='0'&&Ch<='9'){ x=(x<<1)+(x<<3)+(Ch^48); Ch=getchar(); }
return x*f;
}
void write(int x,int sp){
int len=0;
if(x<0){ putchar('-'); x=-x; }
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
} using namespace IO;
const int NN=10010;
int n,m,b[NN],a[60],f[500010];
signed main(){
freopen("subset.in","r",stdin);
freopen("subset.out","w",stdout);
n=read(); m=read();
for(int i=0;i<=m;i++) b[i]=read();
b[0]=0;
while(!b[a[1]]) ++a[1];
f[0]=f[a[1]]=1; --b[a[1]];
for(int i=2;i<=n;i++){
a[i]=a[i-1];
while(!b[a[i]]) ++a[i];
for(int j=m;j>=a[i];j--){
b[j]-=f[j-a[i]];
f[j]+=f[j-a[i]];
}
}
for(int i=1;i<=n;i++) write(a[i],' ');
return puts(""),0;
}
T2 异或
好像能trie树做?
可以枚举每一位,在这一位 \(bit\) 上产生贡献的 \(i\) 与 \(k\) 满足 \(a_i,a_k\) 第 \(bit\) 位之前相同,且在第 \(bit\) 位不同。中间的 \(j\) 第 \(bit\) 位与 \(a_i\) 相同就行。
因此对每一位扫一遍,记下到 \(i\) 为止 \(bit\) 位之前部分为 \(rest\) ,且第 \(bit\) 位为 \(now\) 的数量为 \(num_{i,rest,now}\) ,单纯第 \(bit\) 位为 \(now\) 的数量为 \(sum_{i,now}\) 。
那么对位置 \(k\) ,令它之前与它的 \(rest\) 相同的位置为 \(pos\) ,那么它与他之前的数的贡献即为
记 \(rest\) 意义下 \(\sum_{pos}sum_{pos,now}\) 为 \(pre_{rest,now}\) ,在线更新即可做到复杂度 \(O(30n)\) 。
\(code:\)
T2
#include<bits/stdc++.h>
using namespace std;
namespace IO{
typedef long long LL;
char Ch[50];
int read(){
int x=0,f=1; char Ch=getchar();
while(Ch<'0'||Ch>'9'){ if(Ch=='-') f=-1; Ch=getchar(); }
while(Ch>='0'&&Ch<='9'){ x=(x<<1)+(x<<3)+(Ch^48); Ch=getchar(); }
return x*f;
}
void write(LL x,int sp){
int len=0;
if(x<0){ putchar('-'); x=-x; }
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
} using namespace IO;
const int NN=500010;
int n,tot,a[NN],num[2][NN];
LL ans,sum[2],pre[2][NN];
unordered_map<int,int>has;
void clear(){
has.clear();
tot=sum[0]=sum[1]=0;
memset(num,0,sizeof(num));
memset(pre,0,sizeof(pre));
}
signed main(){
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=30;i;i--){
clear();
for(int j=1;j<=n;j++){
int now=(a[j]>>i-1)&1,rest=a[j]>>i;
if(!has[rest]) has[rest]=++tot;
rest=has[rest];
ans+=sum[now^1]*num[now^1][rest]+pre[now^1][rest];
++num[now][rest]; ++sum[now];
pre[now][rest]-=sum[now];
}
}
write(ans,'\n');
return 0;
}
T3 异或2
推式子加高精的离谱题。

记忆化搜索。 map可以用哈希映射。(好像也可以不用
\(code:\)
T3
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
char Ch[50];
int read(){
int x=0,f=1; char Ch=getchar();
while(Ch<'0'||Ch>'9'){ if(Ch=='-') f=-1; Ch=getchar(); }
while(Ch>='0'&&Ch<='9'){ x=(x<<1)+(x<<3)+(Ch^48); Ch=getchar(); }
return x*f;
}
void write(int x,int sp){
int len=0;
if(x<0){ putchar('-'); x=-x; }
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
} using namespace IO;
namespace Big_Integers{
const int base=1e16;
typedef __uint128_t ULL;
struct big{
int l,c[100];
ULL has;
big(){}
big(int x){ memset(c,0,sizeof(c)); l=1; c[1]=x; }
void calc(){ has=l; for(int i=1;i<=l;i++) has*=c[i]; }
void print(){ printf("%lld",c[l]);for(int i=l-1;i>0;i--)printf("%016lld",c[i]);puts(""); }
big operator+(const big& a)const{
big res=big(0); res.l=max(l,a.l);
for(int i=1;i<=res.l;i++){
res.c[i]+=c[i]+a.c[i];
res.c[i+1]+=res.c[i]/base;
res.c[i]%=base;
}
if(res.c[res.l+1]) ++res.l;
res.calc();
return res;
}
big operator-(const int& a)const{
big res=big(0); res.l=l;
for(int i=1;i<=l;i++) res.c[i]=c[i];
int pos=1; res.c[1]-=a;
while(res.c[pos]<0){
res.c[pos]+=base;
++pos; --res.c[pos];
}
if(!res.c[res.l]) --res.l;
res.calc();
return res;
}
big operator*(const int& a)const{
big res=big(0); res.l=l;
for(int i=1;i<=l;i++){
res.c[i]+=c[i]*a;
res.c[i+1]+=res.c[i]/base;
res.c[i]%=base;
}
if(res.c[res.l+1]) ++res.l;
res.calc();
return res;
}
big operator/(const int& a)const{
big res=big(0); res.l=l;
for(int i=1;i<=l;i++) res.c[i]=c[i];
if(res.c[1]&1) --res.c[1];
for(int i=l;i;i--){
if(res.c[i]&1) res.c[i-1]+=base;
res.c[i]/=a;
}
if(!res.c[l]) --res.l;
res.calc();
return res;
}
}n;
big Read(){
big res=big(0);
char ch[1000]; scanf("%s",ch+1);
int len=strlen(ch+1);
for(int i=1;i<=len;i++)
res=res*10+big(ch[i]-'0');
return res;
}
map<ULL,big>f;
} using namespace Big_Integers;
big dfs(big now){
if(now.l<=1&&now.c[1]<=0) return f[now.has]=big(0);
if(f.find(now.has)!=f.end()) return f[now.has];
big k=now/2;
if(now.c[1]&1) f[now.has]=dfs(k)*4+k*6;
else f[now.has]=dfs(k)*2+dfs(k-1)*2+k*4-4;
return f[now.has];
}
signed main(){
freopen("rox.in","r",stdin);
freopen("rox.out","w",stdout);
n=Read();
dfs(n).print();
return 0;
}
T4 卡牌游戏
是没切的原题。。
把每张卡牌看做一条有向边,由 \(a_i\) 指向 \(b_i\) ,那么操作实际上就是把边翻转,使最后每个点的出度都小于等于 \(1\) 。因此可以得出,有解当且仅当每个联通块都是一棵树或基环树。
为了方便,把单向边看作双向边,点权为 \(1\) 代表边从起点指向终点,反之为 \(0\) 。
不难发现,最后状态一定为若干棵(基环)内向树。
于是对每个联通块分别计算。对于树,可以直接换根DP,根由 \(u\) 变为 \(v\) 时需要改变的边只有 \(u\to v\) ,按边权讨论即可。对于基环树,可以讨论环的方向。集体来说,可以先删去环上的一条边按树处理,之后讨论删去的边的方向就行了。
\(code:\)
T4
#include<bits/stdc++.h>
using namespace std;
namespace IO{
#define int long long
#define mpr make_pair
#define fi first
#define se second
typedef pair<int,int>PII;
char Ch[50];
int read(){
int x=0,f=1; char Ch=getchar();
while(Ch<'0'||Ch>'9'){ if(Ch=='-') f=-1; Ch=getchar(); }
while(Ch>='0'&&Ch<='9'){ x=(x<<1)+(x<<3)+(Ch^48); Ch=getchar(); }
return x*f;
}
void write(int x,int sp){
int len=0;
if(x<0){ putchar('-'); x=-x; }
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
} using namespace IO;
const int NN=200010,mod=998244353;
int n,m,ansf,anss;
namespace Graph{
int idx,head[NN];
int en,pn,fr,to,pos,cnt;
int f[NN],g[NN];
bool vis[NN];
vector<int>vec;
struct edge{
int to,nex;
bool w;
}e[NN<<1];
void add(int a,int b){
e[++idx]=(edge){b,head[a],1}; head[a]=idx;
e[++idx]=(edge){a,head[b],0}; head[b]=idx;
}
void check_dfs(int s){
++pn; vis[s]=1;
for(int v,i=head[s];i;i=e[i].nex){
v=e[i].to; ++en;
if(!vis[v]) check_dfs(v);
}
}
void check(){
for(int i=1;i<=m;i++) if(!vis[i]){
en=pn=0; check_dfs(i);
if(en>pn*2) puts("-1 -1"),exit(0);
}
memset(vis,0,sizeof(vis));
}
void pre_dfs(int s,int fa){
vis[s]=1;
for(int v,i=head[s];i;i=e[i].nex) if((v=e[i].to)!=fa){
if(vis[v]){
fr=s; to=v; pos=i;
continue;
}
pre_dfs(v,s);
f[s]+=f[v]+e[i].w;
}
}
void work_dfs(int s,int fa){
vec.push_back(g[s]);
for(int v,i=head[s];i;i=e[i].nex) if((v=e[i].to)!=fa){
if(i==pos||i==(pos^1)) continue;
g[v]=g[s]+(e[i].w?-1:1);
work_dfs(v,s);
}
}
} using namespace Graph;
signed main(){
freopen("card.in","r",stdin);
freopen("card.out","w",stdout);
n=read(); m=n<<1; idx=1; anss=1;
for(int a,b,i=1;i<=n;i++)
a=read(),b=read(),add(a,b);
check();
for(int i=1;i<=m;i++) if(!vis[i]){
fr=to=pos=cnt=0; vec.clear();
pre_dfs(i,0); g[i]=f[i]; work_dfs(i,0);
if(!pos){
sort(vec.begin(),vec.end());
for(int v:vec)
if(v==vec[0]) ++cnt;
else break;
ansf+=vec[0]; (anss*=cnt)%=mod;
} else if(fr==to) ansf+=min(g[fr],g[to]);
else{
if(!e[pos].w) pos^=1;
(e[pos].to==to)?++g[to]:++g[fr];
cnt=1+(g[to]==g[fr]);
ansf+=min(g[to],g[fr]); (anss*=cnt)%=mod;
}
}
write(ansf,' '); write(anss,'\n');
return 0;
}

浙公网安备 33010602011771号