首页 > 其他分享 >AT杂题

AT杂题

时间:2023-11-06 20:25:50浏览次数:28  
标签:const int long maxn 杂题 dp define

1. AT ABC169F

[ABC169F] Knapsack for All Subsets

思路:

考虑对于任意一个集合 \(P| P\in T\) \(P=\{ x1,x2,\cdots xk\} \ A[x1]+A[x2]+\cdots A[xk]=S\) 则其对答案的贡献为\(2^{n-len}\)

则可以设\(dp[i][j]\)表示前 \(i\) 个数中,和为 \(j\) 的答案

则可以列出转移方程\(dp[i][j]=\frac{dp[i-1][j-a[i]]}{2}+dp[i-1][j]\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=3005;
const int mod=998244353;
int n,s;
int a[maxn];
int qp(int x,int k){
	int res=1;
    while(k){
        if(k&1)res=res*x%mod;
        x=x*x%mod;
        k>>=1;
    }
    return res;
}
signed main(){
    cin>>n>>s;
    for(int i=1;i<=n;i++)cin>>a[i];
    dp[0]=1;
    for(int i=1;i<=n;i++){
        for(int j=s;j>=a[i];j--){
            dp[j]=(dp[j-a[i]]*qp(2,mod-2)+dp[j])%mod;
        }
    }
    cout<<dp[s]<<endl;
    return 0; 
}

2. [ABC159F] Knapsack for All Segments

[ABC159F] Knapsack for All Segments

思路:考虑对于一段区间[l,r]满足子序列之和=S,则这段区间对整体的贡献为 \(l\times (n-r+1)\)

我们另 \(dp[i]\) 表示和为 \(i\) 的子序列的左端点之和为 \(dp[i]\)

枚举右端点,用背包进行统计

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=3005;
const int mod=998244353;
int n,S;
int a[maxn];
int dp[maxn];
signed main(){
	cin>>n>>S;
	for(int i=1;i<=n;i++)cin>>a[i];
	int ans=0;
	for(int i=1;i<=n;i++){
		if(a[i]<S)ans=(ans+dp[S-a[i]]*(n-i+1)%mod)%mod;
		if(a[i]==S)ans=(ans+i*(n-i+1)%mod)%mod;
		for(int j=S;j>=a[i];j--)dp[j]=(dp[j]+dp[j-a[i]])%mod;
		dp[a[i]]=(dp[a[i]]+i)%mod;
	}
	cout<<ans<<endl;
	return 0;
}

3. [ABC163E] Active Infants

[ABC163E] Active Infants

思路:考虑一种贪心思路:我们只要将小的全部做成最优解,然后大的也一定是最优解。

将数组 \(a\) 按照值排序,设 \(dp[i][j]\) 表示将 \(1\sim j-i+1\) 放到 区间 \([l,r]\) 的最优策略

可以轻松列出转移方程

这里考虑两个问题:为什么 \(a[i]\) 所放的区间是连续的;为什么要从小的a开始枚举

第一个问题:考虑从一段区间往回倒退。我这段区间中所有要向左移动的,都是后缀的一段最小值;反之亦然。

第二个问题:因为小的a对答案的影响较小,这样能保证之后的答案更优。

好dp题一道

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2005;
int n;
struct node{
	int val,id;
}a[maxn];
int dp[maxn][maxn];
bool cmp(node x,node y){
	return x.val<y.val;
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i].val;
		a[i].id=i;
	}
	sort(a+1,a+n+1,cmp);
	dp[0][0]=1;
	for(int len=1;len<=n;len++){
		for(int i=1;i+len-1<=n;i++){
			int j=i+len-1;
			dp[i][j]=max(dp[i][j],max(dp[i+1][j]+a[j-i+1].val*abs(i-a[j-i+1].id),dp[i][j-1]+a[j-i+1].val*abs(j-a[j-i+1].id)));
		}
	}
	cout<<dp[1][n]<<endl;
	return 0;
}

4. [ABC165F] LIS on Tree

[ABC165F] LIS on Tree

定义 \(f[i]\) 表示长度为 \(i\) 的最长上升子序列以 \(f[i]\) 结尾

\(ans[i]\) 表示节点 \(i\) 到根的这段路径中的最长上升子序列长度

熟悉的dfs栈,同时使用二分去寻找最大的 \(x\) 使得 \(f[x] \leq a[u]\)

如果 \(x>ans[fa]\) 则 \(ans[x]=ans[fa]+1\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+5;
int n,a[maxn];
vector<int> G[maxn];
int f[maxn],ans[maxn];
void dfs(int u,int fa){
	int l=1,r=ans[fa];
	int lst=0;
	if(!fa)f[1]=a[u],ans[1]=1;
	else{
		while(l<=r){
			int mid=l+r>>1;
			if(a[u]<=f[mid])r=mid-1;
			else l=mid+1;
		}
		lst=f[l];
		f[l]=a[u];
		ans[u]=ans[fa];
		if(l>ans[fa])ans[u]++;
	}
	for(int v:G[u]){
		if(v==fa)continue;
		dfs(v,u);
	}
	f[l]=lst;
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<n;i++){
		int u,v;cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1,0);
	for(int i=1;i<=n;i++)cout<<ans[i]<<endl;
	return 0;
}

5. [ABC167F] Bracket Sequencing

[ABC167F] Bracket Sequencing

思路:想到大方向后其实不难想的正解,但是比较难往贪心上想

考虑贪心,将所有已有的串中成对的括号消掉之后,每一个字符串就会成为 )(() 这样的形式,记每个字符串的左括号和右括号的数量为 \(x,y\)

\(x\) 较大的放在左边,反之右边

最后统一判断一下即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6+5;
int n;
struct node{
    string s;
    int l,r; 
}L[maxn],R[maxn];
int cl,cr;
node get(string s){
    stack<char> st;
    for(int i=0;i<s.size();i++){
        if(s[i]=='(')st.push(s[i]);
        else if(s[i]==')'){
            if(!st.empty()&&st.top()=='(')st.pop();
            else st.push(s[i]);
        }
    }
    int c1=0,c2=0;
    string str="";
    while(!st.empty()){
        char c=st.top();
        st.pop();
        if(c=='(')c1++;
        else c2++;
        str+=c;
    }
    reverse(str.begin(),str.end());
    return {str,c1,c2};
}
bool cmp1(node x,node y){
    return x.r<y.r;
}
bool cmp2(node x,node y){
    return x.l>y.l;
}
bool check(string s){
    stack<char> st;
    for(int i=0;i<s.size();i++){
        if(s[i]=='(')st.push(s[i]);
        else{
            if(!st.empty()&&st.top()=='(')st.pop();
            else st.push(s[i]);
        }
    }
    if(st.empty())return 1;
    else return 0;
}
signed main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        string s;cin>>s;
        auto x=get(s);
        if(x.l>=x.r)L[++cl]=x;
        else R[++cr]=x;
    }
    sort(L+1,L+cl+1,cmp1);
    sort(R+1,R+cr+1,cmp2);
    string ss;
    for(int i=1;i<=cl;i++)ss+=L[i].s;
    for(int i=1;i<=cr;i++)ss+=R[i].s;
    if(check(ss))cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
    return 0;
}

6. [ABC181F] Silver Woods

[ABC181F] Silver Woods

思路:考虑二分

考虑什么情况下钉子会被经过:只要有任意一条路径,使得每段距离小于d,并且联通上下界,则这个直径为 \(d\) 的无法通过

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=105;
const double esp=1e-6;
int n;
struct node{
	int x,y;
}a[maxn*4];
int cnt;
int fa[maxn];
int find(int x){
	if(fa[x]!=x)return fa[x]=find(fa[x]);
	return fa[x];
}
void merge(int u,int v){
	int fu=find(u),fv=find(v);
	if(fu==fv)return ;
	fa[fu]=fv;
	return ;
}
double dis(node a,node b){
	return 1.0*sqrt(1.0*(a.x-b.x)*(a.x-b.x)+1.0*(a.y-b.y)*(a.y-b.y));
}
bool check(double d){
	for(int i=1;i<=cnt;i++)fa[i]=i;
	for(int i=1;i<=cnt;i++)
		for(int j=1;j<=cnt;j++){
			if(i==j)continue;
			if(dis(a[i],a[j])-d>esp)continue;
			merge(i,j);
		}
	for(int i=1;i<=cnt;i++){
		if(a[i].y!=100&&a[i].y!=-100)continue;
		for(int j=1;j<=cnt;j++){
			if(i==j)continue;
			if(a[i].y+a[j].y)continue;
			if(find(i)==find(j))return 0;
		}
	}
	return 1;
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		int x,y;cin>>x>>y;
		a[++cnt]={x,y};
		a[++cnt]={x,100};
		a[++cnt]={x,-100};
	}
	double l=0.0,r=200.0;
	while(r-l>esp){
		double mid=(l+r)/2;
		if(check(mid))l=mid;
		else r=mid;
	}
	printf("%.6f\n",l/2);
	return 0;
}

7. [ABC195F] Coprime Present

[ABC195F] Coprime Present

思路:观察数据范围,考虑在数据范围上做功夫。

\(\gcd(n,m)=\gcd(n-m,m)\) 所以可以得出 \(n-m\leq B-A\)

所以我们对于任意一个 \([0,B-A]\) 的质数,有且仅有一个数能够整除这个数,而 \(72\) 以内的质数只有 \(20\) 个,所以可以状压。复杂度 \(O(len\times 2^{20})\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=75;
int l,r;
int prim[maxn];
bool vis[maxn];
int tot;
int dp[(1<<20)+5];
void init(){
    int mx=72;
    for(int i=2;i<=mx;i++){
        if(!vis[i])prim[++tot]=i;
        for(int j=1;j<=tot&&i*prim[j]<=mx;j++){
            vis[prim[j]*i]=1;
            if(i%prim[j]==0)break;
        }
    }
}
signed main(){
	cin>>l>>r;
	init();
	dp[0]=1;
	for(int i=l;i<=r;i++){
		int t=0;
		for(int j=1;j<=tot;j++)if(i%prim[j]==0)t|=(1<<(j-1));
		for(int s=0;s<(1<<tot);s++){
			if(!(s&t)){
				dp[s|t]+=dp[s];
			}
		}
	}
	int ans=0;
	for(int i=0;i<(1<<tot);i++)ans+=dp[i];
	cout<<ans<<endl;
	return 0;
}

8.[ABC197F] Construct a Palindrome

[ABC197F] Construct a Palindrome

思路:令 \(dis[i][j]\) 表示从 \(1\sim i, j\sim n\) 所能带来的最大贡献,且满足 \(s[1\sim i]=s[n\sim j]\)

考虑建图后跑 \(bfs\) 对于每一个点对 \((x,y)\) ,设 \(x\) 与 \((a,c1)\) 相连,\(y\) 与 \((b,c2)\) 相连

如果 \(c1=c2\) 则将 \((x,y)\) 与 \((a,b)\) 相连。然后跑 \(bfs\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1005;
const int inf=0x3f3f3f3f;
int n,m;
vector<pair<int,int>> G[maxn],g[maxn][maxn];
int dis[maxn][maxn],vis[maxn][maxn];
void bfs(){
	queue<pair<int,int>> Q;
	memset(dis,inf,sizeof(dis));
	vis[1][n]=1;
	dis[1][n]=0;
	Q.push({1,n});
	while(!Q.empty()){
		auto u=Q.front();
		Q.pop();
		for(auto v:g[u.first][u.second]){
			if(!vis[v.first][v.second]){
				vis[v.first][v.second]=1;
				dis[v.first][v.second]=dis[u.first][u.second]+1;
				Q.push({v.first,v.second});
			}
		}
	}
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int a,b;char c;
		cin>>a>>b>>c;
		G[a].push_back({b,c});
		G[b].push_back({a,c});
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			for(auto a:G[i]){
				for(auto b:G[j]){
					if(a.second==b.second){
						g[i][j].push_back({a.first,b.first});
					}
				}
			}
		}
	}
	bfs();
	int ans=inf;
	for(int i=1;i<=n;i++)ans=min(ans,dis[i][i]*2);
	for(int i=1;i<=n;i++){
		for(auto j:G[i]){
			ans=min(ans,dis[i][j.first]*2+1);
		}
	}
	if(ans>=inf)puts("-1");
	else cout<<ans<<endl;
	return 0;
}

9. [ABC209F] Deforestation

[ABC209F] Deforestation

思路:

可以推式子得到:

当 \(a[i-1]>a[i]\) 时,\(i-1\) 在 \(i\) 之前

当 \(a[i]>a[i-1]\) 时,\(i\) 在 \(i-1\) 之前

当 \(a[i]==a[i-1]\) 时,\(i\) 可以放在任意位置

这样可以设计一个 \(dp\)

设 \(dp[i][j]\) 表示第i个数放在 \(j\) 的合法方案数

这里的 \(j\) 表示第 \(i\) 个数放在 \(1\sim i\) 的第 \(j\) 个位置,所以这里仅仅是相对位置

当 \(a[i-1]>a[i]\) \(dp[i][j]=\sum_{k=1}^{j-1}dp[i-1][k]\)

当 \(a[i]>a[i-1]\) \(dp[i][j]=\sum_{k=j}^{i-1}dp[i-1][k]\)

当 \(a[i]==a[i-1]\) \(dp[i][j]=\sum_{k=1}^{i-1}dp[i-1][k]\)

一定注意,这题设计的状态转移是相对位置,一定不能搞错了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=4005;
const int mod=1e9+7;
int n;
int a[maxn];
int dp[maxn][maxn],s[maxn][maxn];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	dp[1][1]=1;
	for(int i=1;i<=n;i++)s[1][i]=1;
	for(int i=2;i<=n;i++){
		for(int j=1;j<=i;j++){
			if(a[i-1]>a[i])dp[i][j]=s[i-1][j-1]%mod;
			if(a[i-1]<a[i])dp[i][j]=(s[i-1][i-1]-s[i-1][j-1]+mod)%mod;
			if(a[i-1]==a[i])dp[i][j]=s[i-1][i-1]%mod;
		}
		for(int j=1;j<=n;j++)s[i][j]=(s[i][j-1]+dp[i][j])%mod;
	}
	cout<<s[n][n]<<endl;
	return 0;
}

10.[ABC217F] Make Pair

[ABC217F] Make Pair

以后看到这种区间删除的问题,一定要考虑能否区间dp

\(dp[i][j]\)表示 \(i\sim j\) 这段区间被消除的种类数

所以可以列出转移方程 \(dp[i][j]=dp[i+1][k-1]*dp[k+1][j]*\binom{\frac{j-i+1}{2}}{\frac{j-k+1}{2}}\)

后面的理解为从 \(\frac{j-i+1}{2}\) 中选 \(\frac{j-k+1}{2}\)

可以感性理解一下

#include<bits/stdc++.h>
#define int long long
#define mem(a) memset(a,0,sizeof(a))
#define set(a,b) memset(a,b,sizeof(a))
#define ls p<<1
#define rs p<<1|1
#define pb(x) push_back(x)
#define pt(x) putchar(x)
#define T int t;cin>>t;while(t--)
#define begin init()
#define rand RAND
#define LOCAL
using namespace std;
template<class Typ> Typ &re(Typ &x){char ch=getchar(),sgn=0; x=0;for(;ch<'0'||ch>'9';ch=getchar()) sgn|=ch=='-';for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+(ch^48);return sgn&&(x=-x),x;}
template<class Typ> void wt(Typ x){if(x<0) putchar('-'),x=-x;if(x>9) wt(x/10);putchar(x%10^48);}
const int inf=0x3f3f3f3f3f;
const int maxn=405;
const int mod=998244353;
int seed = 19243;
unsigned rand(){return seed=(seed*48271ll)%2147483647;}
int fac[maxn],inv[maxn];
int qp(int x,int k){int res=1;while(k){if(k&1)res=res*x%mod;x=x*x%mod;k>>=1;}return res;}
void init(){int mx=maxn-5;fac[0]=1;for(int i=1;i<=mx+1;i++)fac[i]=fac[i-1]*i%mod;inv[mx+1]=qp(fac[mx+1],mod-2);for(int i=mx;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;}
int C(int n,int m){if(n<m||m<0)return 0;return fac[n]*inv[m]%mod*inv[n-m]%mod;}
int n,m;
int vis[maxn][maxn*2];
int dp[maxn][maxn*2];
signed main(){
	begin;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int a,b;cin>>a>>b;
		if((b-a+1)%2==1)continue;
		vis[a][b]=vis[b][a]=1;
		if(a+1==b||b+1==a)dp[a][b]=1;
	}
	n*=2;
	for(int i=1;i<=n+1;i++)dp[i][i-1]=1;
	for(int len=2;len<=n;len+=2){
		for(int i=1;i+len-1<=n;i++){
			int j=i+len-1;
			if(vis[i][j])dp[i][j]=dp[i+1][j-1];
			for(int k=i+1;k<j;k+=2){
				if(vis[i][k]){
					dp[i][j]=(dp[i][j]+dp[i+1][k-1]*dp[k+1][j]%mod*C((j-i+1)/2,(j-k+1)/2)%mod)%mod;
				}
			}
		}
	}
	cout<<dp[1][n]<<endl;
	return 0;
}

11.[ABC209E] Shiritori

[ABC209E] Shiritori

思考一种建图方式

如果一个串的后面无法再接其他串,则这个点为先手必胜点,我们使 \(f[u]=1\)

所以我们可以考虑将这些串建图,然后跑一遍 \(BFS\)

建图怎么建?把所有的单词的后三个字符和前三个字符连起来

然后跑类似 \(topu\) 排序的东西

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+5;
int n;
map<string,int> mp;
vector<int> G[maxn];
int ind[maxn];
int id,f[maxn];
string str[maxn];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		string s;cin>>s;
		int m=s.size()-1;
		string u=s.substr(0,3);
		string v=s.substr(m-2,3);
		if(!mp[u])mp[u]=++id;
		if(!mp[v])mp[v]=++id;
		G[mp[v]].push_back(mp[u]);
		ind[mp[u]]++;
		str[i]=s;
	}
	queue<int> Q;
	for(int i=1;i<=id;i++){
		if(!ind[i])Q.push(i),f[i]=1;
	}
	while(!Q.empty()){
		int u=Q.front();
		Q.pop();
		for(int v:G[u]){
			ind[v]--;
			if(f[u]==1&&f[v]==0)f[v]=-1,Q.push(v);
			if(f[u]==-1&&f[v]==0&&!ind[v])f[v]=1,Q.push(v);
            //这两句是核心:因为两个人都会选择最优策略,所以如果当前串所能连接的串中任意一个为其必胜节点,则他必定选择这个节点
		}
	}
	for(int i=1;i<=n;i++){
		int m=str[i].size()-1;
		string s=str[i];
		string u=s.substr(m-2,3);
		if(f[mp[u]]==1)puts("Takahashi");
		if(f[mp[u]]==-1)puts("Aoki");
		if(f[mp[u]]==0)puts("Draw");
	}
	return 0;
}

12.[ABC229F] Make Bipartite

[ABC229F] Make Bipartite

不太好想

思路:看到有关二分图,先考虑染色

我们令0号节点的颜色为0

所以我们可以设 \(dp[i][j][k]\) 表示第 \(i\) 个点的颜色为 \(j\) ,第 \(1\) 个点的颜色为 \(k\)

可以列出转移方程

\[dp[i][j][k]=min(dp[i-1][op][k]+(j==0?a[i]:0)+(j==op?b[i-1]:0))\\ \]

考虑初始化:\(dp[1][0][0]=a[1],dp[1][1][1]=0\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+5;
int n;
int a[maxn],b[maxn];
int dp[maxn][2][2];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)cin>>b[i];
	for(int i=1;i<=n;i++)for(int j=0;j<=1;j++)for(int k=0;k<=1;k++)dp[i][j][k]=1e15;
	dp[1][0][0]=a[1];
	dp[1][1][1]=0;
	for(int p=0;p<=1;p++){
		for(int i=2;i<=n;i++){
			for(int j=0;j<=1;j++){
				for(int k=0;k<=1;k++){
					dp[i][j][p]=min(dp[i][j][p],dp[i-1][k][p]+(j==0?a[i]:0)+(j==k?b[i-1]:0));
				}
			}
		}
	}
	int ans=LONG_LONG_MAX;
	for(int j=0;j<=1;j++){
		for(int k=0;k<=1;k++){
			ans=min(ans,dp[n][j][k]+(j==k?b[n]:0));
		}
	}
	cout<<ans<<endl;
	return 0;
}

13.[ABC227G] Divisors of Binomial Coefficient

[ABC227G] Divisors of Binomial Coefficient

思路:将题目中的公式拆解

\[\binom{n}{k}=\frac{\prod \limits_{i=n-k+1}^{n}i}{\prod \limits_{i=1}^{k}i} \]

不难发现,上下两个范围是相同

一个数 \(x\) 中,\(>\sqrt{x}\) 的质因数最多只有一个:因为 \((\sqrt{x})^2\geq x\)

所以我们可以筛出分母所有的质因数的个数

然后对于分子的每个数也分解质因数,然后算出两数之差。分母的每一个数分解后,如果没有分解干净,说明剩下的数为质因数。

最后统计答案

#include<bits/stdc++.h>
#define int long long
#define mem(a) memset(a,0,sizeof(a))
#define set(a,b) memset(a,b,sizeof(a))
#define ls p<<1
#define rs p<<1|1
#define pb(x) push_back(x)
#define pt(x) putchar(x)
#define T int t;cin>>t;while(t--)
#define rand RAND
#define LOCAL
using namespace std;
template<class Typ> Typ &re(Typ &x){char ch=getchar(),sgn=0; x=0;for(;ch<'0'||ch>'9';ch=getchar()) sgn|=ch=='-';for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+(ch^48);return sgn&&(x=-x),x;}
template<class Typ> void wt(Typ x){if(x<0) putchar('-'),x=-x;if(x>9) wt(x/10);putchar(x%10^48);}
const int inf=0x3f3f3f3f3f;
const int maxn=1e6+5;
const int mod=998244353;
int seed = 19243;
unsigned rand(){return seed=(seed*48271ll)%2147483647;}
int n,k;
int num[maxn],cnt[maxn];
signed main(){
	cin>>n>>k;
	for(int i=1;i<=k;i++)num[i]=i;
	for(int i=2;i<=maxn-5;i++){
		int x=i;
		for(int j=i;j<=k;j+=i){
			while(num[j]%i==0){
				num[j]/=i;
				cnt[i]--;
			}
		}
	}
	for(int i=1;i<=k;i++)num[i]=i+n-k;
	for(int i=2;i<=maxn-5;i++){
		int x=((n-k)/i+1)*i;
		for(int j=x;j<=n;j+=i){
			while(num[j-n+k]%i==0){
				num[j-n+k]/=i;
				cnt[i]++;
			}
		}
	}
	int ans=1;
	for(int i=2;i<=maxn-5;i++){
		ans*=(cnt[i]+1);
		ans%=mod;
	}
	for(int i=1;i<=k;i++)if(num[i]!=1)ans*=2,ans%=mod;
	cout<<ans%mod<<endl;
	return 0;
}

14.[ABC233F] Swap and Sort

[ABC233F] Swap and Sort

这个题目是一个构造题,可以随意构造

所以我们可以考虑对于每一个点的相邻节点,只要能够得到对应的结果,就可以翻转

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6+5;
int n,m;
int a[maxn];
struct node{
	int v,id;
};
vector<node> G[maxn];
vector<int> ans;
int fa[maxn],vis[maxn];
int find(int u){
	if(u!=fa[u])fa[u]=find(fa[u]);
	return fa[u];
}
void merge(int u,int v){
	int fu=find(u),fv=find(v);
	if(fu==fv)return ;
	fa[fu]=fv;
	return ;
}
bool have_solution(){
	for(int i=1;i<=n;i++){
		if(find(a[i])!=find(i))return 0;
	}
	return 1;
}
struct Node{
	int to,nxt,id;
}E[maxn];
int head[maxn],cnt;
void add(int u,int v,int i){
	E[++cnt]={v,head[u],i};
	head[u]=cnt;
}
bool check(int u,int f,int col){
	if(a[u]==col)return 1;
	for(int i=head[u];i;i=E[i].nxt){
		int v=E[i].to,id=E[i].id;
		if(v==f)continue;
		if(check(v,u,col)){
			ans.push_back(id);
			swap(a[u],a[v]);
			return 1;
		}
	}
	return 0;
}
void dfs(int u){
	vis[u]=1;
	for(int i=head[u];i;i=E[i].nxt){
		int v=E[i].to;
		if(vis[v])continue;
		dfs(v);
	}
	if(!check(u,0,u)){
		puts("-1");
		exit(0);
	}
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)fa[i]=i;
	cin>>m;
	for(int i=1;i<=m;i++){
		int u,v;cin>>u>>v;
		if(find(u)!=find(v)){
			add(u,v,i);
			add(v,u,i);
			merge(u,v);
		}
	}
	if(!have_solution()){
		puts("-1");
		return 0;
	}
	for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
	cout<<ans.size()<<endl;
	for(int u:ans)cout<<u<<" ";
	cout<<endl;
	return 0;
}

15.[ABC238F] Two Exams

[ABC238F] Two Exams

思路:

我们将其排序,然后令 \(rank[a[i]]=b[i]\) 我们从 \(i=1\sim n\) 枚举,所以 \(i\) 肯定是大于 \(i-1\) 的

所以如果想要选 \(i\) 这个节点,我们要满足 \(rand[i]\) 小于之前所选择的 \(rank\)

\(dp[第i个人][已经选择了的人数为len][没有选择的人中rank的最小值]\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=305;
const int mod=998244353;
int n,k;
int p[maxn],q[maxn];
int dp[maxn][maxn][maxn];
map<int,int> mp;
signed main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++)cin>>p[i];
	for(int i=1;i<=n;i++)cin>>q[i];
	for(int i=1;i<=n;i++)mp[p[i]]=q[i];
	dp[0][0][n+1]=1;
	for(int i=1;i<=n;i++){
		int x=mp[i];
		for(int j=0;j<=k;j++){
			for(int y=0;y<=n+1;y++){
				if(j<k&&x<y){
					dp[i][j+1][y]+=dp[i-1][j][y];
					dp[i][j+1][y]%=mod;
				}
				dp[i][j][min(x,y)]+=dp[i-1][j][y];
				dp[i][j][min(x,y)]%=mod;
			}
		}
	}
	int ans=0;
	for(int i=0;i<=n+1;i++)(ans+=dp[n][k][i])%=mod;
	cout<<ans<<endl;
	return 0;
}

16.[ABC235F] Variety of Digits

[ABC235F] Variety of Digits

这题的翻译!!!!

比较正常的计数dp

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e4+5;
const int mod=998244353;
string n;
int m;
int A[maxn];
int pw[maxn];
int dp[maxn][(1<<12)],dp2[maxn][(1<<12)];
map<int,int> mp;
int a[maxn],pos;
void init(){
	int N=1e4;
	pw[0]=1;
	for(int i=1;i<=N;i++)pw[i]=pw[i-1]*10%mod;
}
pair<int,int> dfs(int pos,int st,int lead,int lim){
	if(pos==-1)return {(st==(1<<m)-1),0};
	if(!lead&&!lim&&dp[pos][st]!=-1)return {dp[pos][st],dp2[pos][st]};
	int up=lim?a[pos]:9;
	int sum1=0,sum2=0;
	for(int i=0;i<=up;i++){
		if(lead&&i==0){
			auto k=dfs(pos-1,st,1,lim&&i==a[pos]);
			sum1=(sum1+k.first)%mod;
			sum2=(sum2+pw[pos]*i%mod*k.first%mod+k.second)%mod;
		}
		else if(mp[i]){
			auto k=dfs(pos-1,st|(1<<(mp[i]-1)),0,lim&&i==a[pos]);
			sum1=(sum1+k.first)%mod;
			sum2=(sum2+pw[pos]*i%mod*k.first%mod+k.second)%mod;
		}
		else{
			auto k=dfs(pos-1,st,0,lim&&i==a[pos]);
			sum1=(sum1+k.first)%mod;
			sum2=(sum2+pw[pos]*i%mod*k.first%mod+k.second)%mod;
		}
	}
	if(!lead&&!lim){
		dp[pos][st]=sum1;
		dp2[pos][st]=sum2;
	}
	return {sum1,sum2};
}
pair<int,int> solve(string x){
	pos=0;
	int len=x.size();
	for(int i=len-1;i>=0;i--)a[pos++]=x[i]-'0';
	return dfs(pos-1,0,1,1);
}
signed main(){
	init();
	cin>>n>>m;
	for(int i=1;i<=m;i++)cin>>A[i];
	for(int i=1;i<=m;i++)mp[A[i]]=i;
	memset(dp,-1,sizeof(dp));
	memset(dp2,-1,sizeof(dp2));
	auto ans=solve(n);
	cout<<ans.second<<endl;
	return 0;
}

17.Star MST

Star MST

好题一道

先考虑将一个以1为根的菊花图,删除 \(1\sim u\),加入 \(u\sim v\) 。设 \(1\sim u\) 的边权为 \(x\),\(u\sim v\) 的边权为 \(y\),当前的最小生成树的大小为 \(t\)

\[t-x+y\geq t\\ x\leq y \]

所以可以得出,如果是这个生成树为最小生成树,则离任意 \(u\) 最近的之一的点,必定为 \(1\)

所以可以得出一个比较显然的DP。设 \(dp[i][j]\) 表示当前已经确定了 \(i\) 个点,且这 \(i\) 个点中与 \(1\) 的距离最大的为 \(j\)

我们去枚举一个转移点 \(t\) 即为前 \(i\) 个点中有 \(t\) 个小于 \(j\) 的节点,则可以得出 \(dp[i][j]=\sum\limits_{k=0}^{j-1}dp[t][k]\)

之后我们还要从 \(n-t\) 个节点中放入 \(i-t\) 个节点。所以 \(dp[i][j]=dp[i][j]\times C_{n-t}^{i-t}\)

然后我们加入这 \(i-t\) 个节点的时候,因为我们在之前的转移当中已经确定了 \(1\sim i\) 的边权,所以只要确定这其余\((i-t)\times(t-1)+\frac{(i-t)\times(i-t-1)}{2}\) 条边的权值,每条边的权值有 \(K-j+1\) 种可能

所以转移方程为:

\[dp[i][j]=\sum\limits_{k=0}^{j-1}dp[t][k]\times C_{n-t}^{i-t}\times (K-j+1)^{(i-t)*(t-1)+\frac{(i-t)\times (i-t-1)}{2}}\\ \]

然后我们会发现一个问题,对于剩余的 \(n-i\) 个未插入的点,他们与之前的点会有更多的限制条件

#include<bits/stdc++.h>
#define int long long
#define mem(a) memset(a,0,sizeof(a))
#define set(a,b) memset(a,b,sizeof(a))
#define ls p<<1
#define rs p<<1|1
#define pb(x) push_back(x)
#define pt(x) putchar(x)
#define T int t;cin>>t;while(t--)
#define begin init()
#define rand RAND
#define LOCAL
using namespace std;
template<class Typ> Typ &re(Typ &x){char ch=getchar(),sgn=0; x=0;for(;ch<'0'||ch>'9';ch=getchar()) sgn|=ch=='-';for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+(ch^48);return sgn&&(x=-x),x;}
template<class Typ> void wt(Typ x){if(x<0) putchar('-'),x=-x;if(x>9) wt(x/10);putchar(x%10^48);}
const int inf=0x3f3f3f3f3f;
const int maxn=255;
const int mod=998244353;
int seed = 19243;
unsigned rand(){return seed=(seed*48271ll)%2147483647;}
int fac[maxn],inv[maxn];
int qp(int x,int k){int res=1;while(k){if(k&1)res=res*x%mod;x=x*x%mod;k>>=1;}return res;}
void init(){int mx=maxn-5;fac[0]=1;for(int i=1;i<=mx+1;i++)fac[i]=fac[i-1]*i%mod;inv[mx+1]=qp(fac[mx+1],mod-2);for(int i=mx;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;}
int C(int n,int m){if(n<m||m<0)return 0;return fac[n]*inv[m]%mod*inv[n-m]%mod;}
int n,k;
int dp[maxn][maxn],sum[maxn][maxn];
signed main(){
	begin;
	cin>>n>>k;
	dp[1][0]=1;
	for(int i=0;i<=k;i++)sum[1][i]=1;
	for(int i=2;i<=n;i++){
		for(int j=0;j<=k;j++){
			for(int t=1;t<i;t++){
				dp[i][j]=(dp[i][j]+sum[t][j-1]*C(n-t,i-t)%mod*qp(k-j+1,(i+t-3)*(i-t)/2)%mod)%mod;
			}
			sum[i][j]=(sum[i][j-1]+dp[i][j])%mod;
		}
	}
	cout<<sum[n][k]<<endl;
	return 0;
}
/*
dp[i][j]表示前i个数,最大值为j
*/

标签:const,int,long,maxn,杂题,dp,define
From: https://www.cnblogs.com/Candycar/p/17813609.html

相关文章

  • 【杂题乱写】AtCoder-ARC115
    AtCoder-ARC115_FMigration*把问题转化成在某个限制\(mid\)下求初始局面和最终局面能到达的最小代价局面,如果相等则说明可达。比较局面的方式是比较权值,如果相等按字典序比较。对每个节点\(u\)求出权值比\(u\)小或权值与\(u\)相等且编号比\(u\)小的节点中,与\(u\)......
  • 杂题记录
    杂题记录记录一些没啥分类的妙妙题[ABC225F]StringCardsdate:2023.10.23初看题目,感觉直接排序,但是发现,\(k\)其实是影响的,也就是直接排序并不一定最优简单的反例22babbba>bab但是b在ba之前不能快排了但是我们发现数据很小返回排序的源头选择排序每次交换两个......
  • 10月杂题记
    CF1875D我们经过思考,容易得出以下结论:如果当前$mex=x$,则下一个删的数一定小于$x$。如果$mex=0$,那么我们就可以不往下算了,因为它们对答案的贡献为$0$。我们设$f[i]$表示当$mex=i$时,$m$的值。则有:$$f[i]=\min(f[j]+(c[i]-1)\timesj+i,f[i])$$其......
  • CF 杂题集1 2200~2400
    updon2023.11.02初稿updon2023.11.04修正部分表达感觉这一套题质量都很不错,有比较好的思维难度,又不是特别难(当然,对于我来说很难),而且有一些比较好的思路和套路。题目链接均为洛谷链接。CF1474DCleaning摘要:性质:考虑端点,发现一定可以从左右两侧开始消除。分别维......
  • 「杂题乱写」Ynoi
    「杂题乱写」Ynoi点击查看目录目录「杂题乱写」YnoiP7710[Ynoi2077]stdmxeypzP5356[Ynoi2017]由乃打扑克P5309[Ynoi2011]初始化P5046[Ynoi2019模拟赛]YunolovessqrttechnologyI由于Ynoi道道卡常且常数受一些常量影响较大,为方便表示复杂度,本文记阈值为\(B\)......
  • 杂题练习
    stl众所周知一般来说,随着社会经济的不断发展,stl越来越成为一款强大的工具。著名cp选手i_wish_a_gilrfriend曾说过:stl,启动!无敌山鸡王说:我在学习了算法近一年后才了解stl,这是我的巨大损失。五星上将麦克阿瑟曾说过,如果上帝不让我使用stl,那我将用枪指向上帝。下面是练习使用stl......
  • 「Note」CF 杂题集 6
    前言难度:CF2600-2700(有一道是2500)别问我为啥没有1到5。\(\color{blueviolet}{CF1473F}\)此题是坏题,他卡你空间。每一个元素有选或不选两种状态,并且有依赖项,元素的贡献有正负,数据范围不大,可以自然联系到最大权闭合子图,采用最小割模型。建模方式如下:对于一个正权点\(u......
  • 【杂题乱写】AtCoder-ARC114
    AtCoder-ARC114_ANotcoprime\(50\)内的质数只有\(15\)个,可能的答案也就只有\(2^{15}\)个,直接枚举。提交记录:Submission-AtCoderAtCoder-ARC114_BSpecialSubsets就是\(i\)与\(f_i\)连边,每个连通块都是基环树,一定能剥叶子变成环,所以答案就是连通块非空子集个数。......
  • DP技巧与DP杂题
    DP常用技巧增加维数交换答案与状态可行解转最优解删掉本质相同的状态对部分状态\(dp\)遇到转移顺序的困难,考虑记忆化搜索遇到转移细节过多的问题,考虑从\(i\rightarrowi+1\)而不是\(i-1\rightarrowi\)考虑状态时,先把需要记下来的都记一遍,再考虑优化DP杂题CF83......
  • 信息安全杂题选讲
          ......