• 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\) 个集合是满足条件的,而 \(j\)\(i-1\) 种选法,集合 \(i,j\)\(2^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+1\)\(lca(p_i,p_{i+1})-1\)

    \(verdandi\) 联手 \(vp\)The 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

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

  • 相关阅读:
    系统进程查看 --- 微软官方出品
    Chrome 浏览器网页保存为PDF文件
    Chrome Google浏览器下载
    最强Android书 架构大剖析 作者网站
    腾讯暑期夏令营之旅
    轨迹记录App是怎样对定位轨迹进行过滤、优化和平滑处理的
    android-8~23 View.java
    android-23 View.java
    The server encountered an internal error that prevented it from fulfilling this request.
    Android Event
  • 原文地址:https://www.cnblogs.com/syzf2222/p/15681683.html
Copyright © 2020-2023  润新知