Processing math: 0%

12 月 cls 记录

2021.12.13:

南京大屠杀纪念日,默哀。


HNOI2011 卡农

十年前的题我也是望尘莫及,哈哈!这是一个神奇的 DP 题。

考虑它的条件:在 n 的所有子集中挑出 m 个,

  1. 任意集合非空;

  2. 任意集合最多出现一次;

  3. 任意数字出现次数为偶数次。

f_i 为挑出了 i 个集合满足条件的方案数。

这样的状态看似不合理,没有办法毫无联系,难以转移,却可以通过容斥转移。

考虑只要确定了前面的,由第三条就可以确定最后一个,故此时方案为 P^{i-1}_{2^n-1}

考虑第一条,即前面的集合不能恰好满足每一个都是偶数,所以减去 f_{i-1}

考虑第二条,假设前面有一个 j 使得集合 i,j 相等,那么此时剩下的 i-2 个集合是满足条件的,而 ji-1 种选法,集合 i,j2^n-1-(i-2) 种选法,故减去 (i-2)(2^n-i+1)f_{i-2}

综上,f_i=P^{i-1}_{2^n-1}-f_{i-1}-(i-2)(2^n-i+1)f_{i-2}

注意,我们一直是按有序算的,故最后还要除以 m!

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=1e6+10;
const int mod=1e8+7;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
inline int ksm(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=1ll*res*x%mod;
		x=1ll*x*x%mod;y>>=1;
	}return res;
}
int n,m,f[maxn],fac=1;
int main(){
	n=read(),m=read();
	int pw=ksm(2,n),cur=pw-1;
	f[0]=1,f[1]=0;
	for(int i=2;i<=m;i++){
		f[i]=((cur-f[i-1]-1ll*(i-1)*(pw-i+1)%mod*f[i-2]%mod)%mod+mod)%mod;
		cur=1ll*cur*(pw-i)%mod;fac=1ll*fac*i%mod;
	}printf("%d\n",1ll*f[m]*ksm(fac,mod-2)%mod);
    return 0;
}

深深地感到自己的弱小。


2021.12.21:

考的 pnershy 的题,不想改。第一题是三维偏序,别人都用的二维树状数组,只有我傻乎乎地用 cdq,第二题是 bitset 优化二进制分组背包,而我只会傻乎乎地分治 ntt。学到了,判断和为 n 的一堆数能否组成 k 的最优复杂度不是 n\log^2 n 而是 n\sqrt n\log n/w

补了昨天 demonlover 的题。其中的 cf1120d 比较有意思。每个节点相当于操作一个区间,也相当于操作差分数组的两个点,所以想到最小生成树。判断每条边有无可能在最小生成树上,就是对于每一个权值,之前的随意取一棵最小的,然后判断当前可不可以加入。是个有意思的小 trick

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
#define ll long long
int n,m,w[maxn],tot,ok[maxn];ll ans;
int beg[maxn],nex[maxn<<1],to[maxn<<1],e;
inline void add(int x,int y){
	e++;nex[e]=beg[x];beg[x]=e;to[e]=y;
	e++;nex[e]=beg[y];beg[y]=e;to[e]=x;
}
int l[maxn],r[maxn],ti,f[maxn];
struct node{int x,y,z,id;}E[maxn];
inline void dfs(int x,int fa){
	int flag=0;l[x]=ti+1;
	for(int i=beg[x];i;i=nex[i]){
		int t=to[i];
		if(t==fa)continue;
		dfs(t,x);flag=1;
	}if(!flag)++ti;r[x]=ti;
	E[x]=(node){l[x],r[x]+1,w[x],x};
	//printf("%d [%d,%d]\n",x,l[x],r[x]);
}
inline int cmp(node x,node y){return x.z<y.z;}
inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
int main(){
	freopen("crops.in","r",stdin);
	freopen("crops.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++)
		w[i]=read();
	for(int i=1,x,y;i<n;i++)
		x=read(),y=read(),add(x,y);
	dfs(1,0);sort(E+1,E+1+n,cmp);
	for(int i=1;i<=n;i++)f[i]=i;
	for(int i=1,j=1;i<=n;i=j){
		for(j=i+1;j<=n&&E[i].z==E[j].z;j++);
		for(int k=i;k<j;k++)
			if(find(E[k].x)!=find(E[k].y))
				tot++,ok[E[k].id]=1;
		for(int k=i;k<j;k++)
			if(find(E[k].x)!=find(E[k].y))
				ans+=E[k].z,f[find(E[k].x)]=find(E[k].y);
	}printf("%lld %d\n",ans,tot);
	for(int i=1;i<=n;i++)
		if(ok[i])printf("%d ",i);puts("");
    return 0;
}

补了一下之前 vp 的 Educational Round 94,里面的 F 和 G 比较有意思。

CF1400G Mercenaries

有时候真的是奇奇怪怪的题目做多了,反而想不到最简单、最本真、最自然的想法。

看到 m\le 20,我还在想什么呢,容斥啊!Ans=\sum\limits_{T\subseteq S} (-1)^{|T|+1} f(T),其中 f(T) 表示硬选这些边的方案数。

剩下的部分就是容易的了,还有就是要预处理一个前缀和。

补了之前考时没做出来的题,喜提一场绿。CF1617E Christmas Chocolates

抽象成一个图论问题。无向图,无穷个点,若 x+y=2^k 则有边,求 n 个特殊点中两两最短路的最大值。

因为是无向图,我们考虑大的往小的连边。我们惊喜地发现这样的边只有一条,即 2^k 恰好大于 x 时有解。易证。

于是原图退化成了一棵 n\log n 级别个点的树。求两叶子节点间的最大距离即可。随意实现。我的 bfs 实现比较诡异。

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
#define fi first
#define se second
#define pii pair<int,int>
#define mkp make_pair
map<int,pii>mp;
inline int f(int x){for(int i=0;;i++)if((1<<i)>=x)return (1<<i);}
int n,m,k,s,u,v,ans;
int main(){
	n=read();
	for(int i=1,x;i<=n;i++)
		x=read(),mp[x]=mkp(0,i);
	while(k=(--mp.end())->fi){
		s=f(k)-k;
		if(mp.find(s)!=mp.end()&&ans<mp[s].fi+mp[k].fi+1){
			ans=mp[s].fi+mp[k].fi+1;u=mp[s].se,v=mp[k].se;
		}if(mp[s].fi<mp[k].fi+1)mp[s]=mkp(mp[k].fi+1,mp[k].se);
		mp.erase(k);
	}printf("%d %d %d\n",u,v,ans);
    return 0;
}

复习了 \text{AC} 自动机。做了 P3121 [USACO15FEB]Censoring G


2021.12.22:

上午考 codycode 的比赛,\text{T3} 简单题,注意 dp 函数的单调性即可。

AC 机题意外收获一个树上 trick,给一堆节点到跟的路径加 1:按 dfs 序排序,p_i+1lca(p_i,p_{i+1})-1

verdandi 联手 vpThe 2021 China Collegiate Programming Contest (Harbin)题解

补了一下昨天的鸽掉的 cf1400f,感觉最近碰到的 \text{AC} 自动机题好多,晕。

大概就是字符串和数论的结合是不好做的,考虑到 X 极小,不妨生成所有的 X-prime 串。这时题意变为最少删除几个使不包含任一生成串。对生成串建 AC 自动机,设 dp_{i,j} 表示考虑到第 i 位,此时在自动机上走到节点 j 所需的最小删除个数。两种情况,若下一个删除则有 dp_{i,j}+1\rightarrow dp_{i+1,j},否则若下一个节点不是 endpos 则有 dp_{i,j}\rightarrow dp_{i+1,ch[j][s[i]-'0']} ,数组滚一下。

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
const int maxn=1e6+10;
const int mod=1e9+7;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int n,m,k,dp[2][maxn];
int ch[maxn][10],cnt,fa[maxn],edp[maxn];
inline void insert(string ss){
	int u=0;
	for(int i=0;i<ss.size();i++){
		int t=ss[i]-'0';
		if(!ch[u][t])ch[u][t]=++cnt;
		u=ch[u][t];
	}edp[u]=1;
}
inline void chk(string ss){
	for(int i=0;i<ss.size();i++){
		int S=0;
		for(int j=i;j<ss.size();j++){
			S+=ss[j]-'0';
			if(k>S&&k%S==0)return;
		}
	}insert(ss);
}
inline void gen(string ss,int S){
	if(S>k)return;
	if(S==k)return void(chk(ss));
	for(int i=1;i<=9;i++)
		gen(ss+(char)(i+'0'),S+i);
}
queue<int>q;
inline void build(){
	for(int i=0;i<=9;i++)
		if(ch[0][i])q.push(ch[0][i]);
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=0;i<=9;i++){
			int &v=ch[x][i];
			if(v)fa[v]=ch[fa[x]][i],q.push(v);
			else v=ch[fa[x]][i];
		}
	}
}
string s;
inline void chkmin(int &x,int y){x=(x<y?x:y);}
int main(){
	cin>>s;n=s.size();
	k=read();gen("",0);//puts("over");
	build();
	memset(dp,0x3f,sizeof(dp));
	dp[0][0]=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=cnt;j++)dp[i&1][j]=inf;
		for(int j=0;j<=cnt;j++){
			chkmin(dp[i&1][j],dp[~i&1][j]+1);
			if(!edp[ch[j][s[i-1]-'0']])
				chkmin(dp[i&1][ch[j][s[i-1]-'0']],dp[~i&1][j]);
		}
	}int ans=inf;
	for(int i=0;i<=cnt;i++)
		chkmin(ans,dp[n&1][i]);
	printf("%d\n",ans);
    return 0;
}

2021.12.23:

做了一道 usaco 的题 [USACO21FEB] No Time to Dry P

尽量多涂,对于相邻的两个相同的,若其中间没有比它们小的就可以减少一次,预处理出这样的区间,容斥一手即可。

posted @   syzf2222  阅读(61)  评论(0编辑  收藏  举报
编辑推荐:
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
阅读排行:
· 如何给本地部署的DeepSeek投喂数据,让他更懂你
· 超详细,DeepSeek 接入PyCharm实现AI编程!(支持本地部署DeepSeek及官方Dee
· 用 DeepSeek 给对象做个网站,她一定感动坏了
· .NET 8.0 + Linux 香橙派,实现高效的 IoT 数据采集与控制解决方案
· .NET中 泛型 + 依赖注入 的实现与应用
点击右上角即可分享
微信分享提示