agc036题解

用于博主过于菜鸡,不会E,所以只能讲A,B,C,D,F了
A,B好像大家都会,就不讲了
C:n个数记为k1k_1k1 ~knk_nkn,那么可以成功实现当且仅当:
{∑i=1nki=3∗mki∈[0,2∗m]奇数的个数小于等于m且与m模2同余\left\{\begin{matrix} \sum_{i=1}^{n}k_i=3*m\\ k_i\in[0,2*m]\\ 奇数的个数小于等于m且与m模2同余 \end{matrix}\right.i=1nki=3mki[0,2m]mm2
于是稍微容斥和组合数一下即可

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int Mod=998244353;
int n,m;
#define Maxn 3000010
int fact[Maxn],inv[Maxn];
inline int C(int i,int j){return 1ll*fact[i]*inv[i-j]%Mod*inv[j]%Mod;}
inline int calc(int x){return C(x+n-1,n-1);}
int main(){
	scanf("%d%d",&n,&m);
	fact[0]=1;
	for(register int i=1;i<=3000000;++i)fact[i]=1ll*fact[i-1]*i%Mod;
	inv[0]=inv[1]=1;
	for(register int i=2;i<=3000000;++i)
		inv[i]=1ll*(Mod-Mod/i)*inv[Mod%i]%Mod;
	for(register int i=2;i<=3000000;++i)inv[i]=1ll*inv[i-1]*inv[i]%Mod;
	int Ans=0;
	for(register int i=0;i<=min(m,n);++i)
	if((m-i)%2==0){
		int at=(3*m-i)/2;
		int res=calc(at);
		res=(res-1ll*i*C(at-m+n-1,n-1)%Mod+Mod)%Mod;
		if(at>m)res=(res-1ll*(n-i)*C(at-m+n-2,n-1)%Mod+Mod)%Mod;
		res=1ll*res*C(n,i)%Mod;
		Ans=(Ans+res)%Mod;
	}
	printf("%d\n",Ans);
	return 0;
}

D:
题意:n个点,如果i<j,i->j有一条-1的边
如果i>j,i->j有一条1的边
以上两种边可删,且删除有一定代价(非负)
i->i+1恒有一条长度为0的边,不可删除
问最少多少代价删边可以使得图中不存在负环
解法:这是一道神题
考虑一张图不存在负环的等价条件,便是可以为这张图的每个点安排一个点权qiq_iqi
使得对于任意一条边(u,v,w),有qu+w≥qvq_u+w\geq q_vqu+wqv
证明:若无负环,每个点有最短路,q取最短路即可
若有q,那么对于一个环中的每条边,变形得w≥qv−quw\geq q_v-q_uwqvqu
右边的和为0,于是肯定非负
于是考虑确定一个序列q,并以此确定此时的最小代价
由于i到i+1总有一条为0的边
故我们有qi≥qi+1q_i\geq q_{i+1}qiqi+1
不妨令pi=qi−qi+1p_i=q_i-q_{i+1}pi=qiqi+1,则pi非负p_i非负pi
i->j为-1的边在图中,需满足∑k=ij−1pk≥1\sum_{k=i}^{j-1}p_k\geq1k=ij1pk1
i->j为1的边在图中,需满足∑k=ji−1pk≤1\sum_{k=j}^{i-1}p_k\leq 1k=ji1pk1
于是dp一下即可O(n3)O(n^3)O(n3)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
int n;
#define Maxn 505
ll val1[Maxn][Maxn];//below
ll val2[Maxn][Maxn];//up
ll f[Maxn][Maxn];
ll sum[Maxn];
inline int calc(int x){return x*(x+1)/2;}
inline void rd(int &x){
	x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
}
int main(){
	ll All=0;
	rd(n);
	int x;
	for(register int i=1;i<=n;++i)
	    for(register int j=1;j<=n;++j)
	       if(j!=i){
	       	    rd(x);
	       	    All+=x;
	       	    if(j<i)val1[j][i-1]=x;
	       	    else val2[i][j-1]=x;
		   }
	n--;
	for(register int i=1;i<=n;++i)
	    for(register int j=2;j<=i;++j){
	    	val1[j][i]+=val1[j-1][i];
	    	val2[j][i]+=val2[j-1][i];
		}
	ll Ans=0;
	ll Ha=0;
	for(register int i=1;i<=n;++i){
		Ha+=val1[i][i];
	    f[i][0]=Ha+val2[i][i];
		for(register int j=0;j<i;++j){
			ll tmp=0;
		    for(register int k=i+1;k<=n;++k){
		    	f[k][i]=max(f[k][i],f[i][j]+tmp+val1[k][k]-val1[i][k]+val2[k][k]);
                tmp+=val1[k][k]-val1[j][k]+val2[i][k];
			}
		}
	}
	for(register int i=1;i<=n;++i)sum[i]=val1[i][i]+sum[i-1];
	for(register int i=1;i<=n;++i)
	    for(register int j=i+1;j<=n;++j){
	    	val1[i][j]+=val1[i][j-1];
	    	val2[i][j]+=val2[i][j-1];
		}
	for(register int i=1;i<=n;++i)
	    for(register int j=0;j<i;++j)
	    	Ans=max(Ans,f[i][j]-val1[j][n]+val1[j][i]+val2[i][n]-val2[i][i]+sum[n]-sum[i]);
    printf("%lld\n",All-Ans);
	return 0;	    
}

F
题意:有多少种排列p,值域为[0,2∗n)[0,2*n)[0,2n),满足对于任意i,n2≤i2+pi2≤4∗n2n^2\leq i^2+p_i^2\leq 4*n^2n2i2+pi24n2
解法:对于每个i确定上下界,考虑容斥
容斥至少k个在下界内的
至于如何求方案数,博主不想写了,去看官方题解吧。
总之,第一步容斥是关键,后面的处理也很巧妙
agc杀死我

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int n,Mod;
#define Maxn 605
int dp[Maxn][Maxn];
int g[Maxn];
struct Data{
	int val,id;
	bool operator <(const Data &z)const{return val==z.val?id>z.id:val<z.val;}
}seq[Maxn<<1];
int calc(int K){
	int tmp=0;
	dp[0][0]=1;
	for(register int i=1;i<=2*n;++i){
		if(seq[i].id>=n)tmp++;
		for(register int j=0;j<=i-tmp;++j){
			if(seq[i].id>=n)dp[i][j]=1ll*dp[i-1][j]*(seq[i].val-j-tmp+2+Mod)%Mod;
			else{
				dp[i][j]=0;
				if(j!=i-tmp)dp[i][j]=(dp[i][j]+1ll*dp[i-1][j]*(g[seq[i].id]-n-K-i+tmp+j+2+Mod))%Mod;
				if(j)dp[i][j]=(dp[i][j]+1ll*dp[i-1][j-1]*(seq[i].val-j-tmp+2))%Mod;
			}
		}
	}
	return dp[2*n][K];
}
int main(){
	scanf("%d%d",&n,&Mod);
	for(register int i=0;i<n;++i){
		seq[i+1]=(Data){floor(sqrt(n*n-i*i-1)),i};
		g[i]=floor(sqrt(4*n*n-i*i));
		if(i==0)g[i]=2*n-1;
    }
    for(register int i=n;i<2*n;++i)seq[i+1]=(Data){floor(sqrt(4*n*n-i*i)),i};
    sort(seq+1,seq+2*n+1);
    int Ans=0;
    for(register int i=0;i<=n;++i){
    	if(i&1)Ans=(Ans-calc(i)+Mod)%Mod;
    	else Ans=(Ans+calc(i))%Mod;
	}
	printf("%d\n",Ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值