冲刺国赛7.27

T1

把答案写成来就是 \(2\sum\limits_{i=1}^n ip_i-ip_i^2\) 要最大化这个东西

\(f_i(x)=ix-ix^2\) 求导变成 \(f'_i(x)=i-2ix\)

因为要最大化加和,所以每个函数的导数都是相等的,否则可以将变化率小的 \(x\) 变小,再将变化率大的 \(x\) 变大

设这个相等的值为 \(v\) ,于是 \(p_i=\frac{1}{2}-\frac{v}{2i}\)

因为 \(p_i\) 的加和为 \(1\) ,所以能解出 \(v\) ,但是这样 \(p_i\) 可能会是负数,所以让一段前缀为 \(0\) ,二分去查找那个位置

设这个位置为 \(be\) 那么 \(v=\frac{n-be-1}{s_n-s_{be-1}}\) 其中 \(s_i\)\(\frac{1}{i}\) 的前缀和

然后再检验一下是否有负数,就能 \(O(1)\) 计算出答案了

Code
#include<bits/stdc++.h>
#define int long long
#define double long double
#define inf 0x3f3f3f3f3f3f3f3f
#define meow(args...) fprintf(stderr,args)
using namespace std;
inline 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;
}
int T,n;
double s[1000010];
inline void solve(){
	n=read();
	int l=1,r=n,be;double res=1;
	while(l<=r){
		int mid=(l+r)>>1;
		double v=1.0L*(n-mid-1)/(s[n]-s[mid-1]);
		if(v<=1.0L*mid) r=mid-1,res=v,be=mid;else l=mid+1;
	}
	printf("%.8Lf\n",res-res/2.0L*(n-be+1)+1.0L*(be+n)*(n-be+1)/4.0L);
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	freopen("sample.in","r",stdin);
	freopen("sample.out","w",stdout);
	for(int i=1;i<=1000000;i++) s[i]=s[i-1]+1.0L/i;
	T=read();while(T--) solve();
	return 0;
}

T2

首先给矩阵行差分一下,再列差分一下

发现只在四个位置修改了,分别是 \((l,l)+1,(l,r+1)-1,(r+1,l)-1,(r+1,r+1)+1\)

发现这跟矩阵树的基尔霍夫矩阵很像,于是我们可以直接求原图的生成树个数

考虑到 \(m\leq n+300\) 所以将一度点和二度点缩起来变成一条边,记录下来选择的和不选择的方案数

一度点可以直接删除,缩二度点时,直接向两边延伸,找到所在的链或者环

链的话直接新连一条边,选择的方案数为 \(1\) ,不选的方案数为链长加一

环的话直接给答案乘上环长

最后再用剩下的点再用行列式求

Code
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
#define inf 0x3f3f3f3f3f3f3f3f
#define meow(args...) fprintf(stderr,args)
using namespace std;
inline 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;
}
int n,m,und=1,pe,base=1,p,S,T;
int a[610][610],deg[500010],id[500010],idx;
int head[500010],ver[1000010],to[1000010],tot=1;
int fa[500010];
struct E{int x,y,v;}e[500010];
queue<int>q;
bool vis[500010];
int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
inline void merge(int x,int y){
	x=getfa(x),y=getfa(y);
	if(x==y) return ;fa[x]=y;
}
inline void add(int x,int y){ver[++tot]=y;to[tot]=head[x];head[x]=tot;}
inline int qpow(int x,int k){
	int res=1,base=x;
	while(k){if(k&1) res=res*base%mod;base=base*base%mod;k>>=1;}
	return res;
}
inline int det(int n){
	int res=1;
	for(int i=1,inv;i<=n;i++){
		for(int j=i+1;j<=n&&!a[i][i];j++) if(a[j][i]) swap(a[i],a[j]),res=-res;
		if(!a[i][i]) return 0;inv=qpow(a[i][i],mod-2);
		for(int j=i+1,t;j<=n;j++){
			t=a[j][i]*inv%mod;
			for(int k=i;k<=n;k++) a[j][k]=(a[j][k]-a[i][k]*t%mod+mod)%mod;
		}
	}
	for(int i=1;i<=n;i++) res=res*a[i][i]%mod;
	return (res+mod)%mod;
}
void dfs(int x){
	if(deg[x]!=2) return S?T=x:S=x,void();vis[x]=1;p++;
	for(int i=head[x],y;i;i=to[i]) if(!vis[y=ver[i]]) dfs(y);
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	freopen("det.in","r",stdin);
	freopen("det.out","w",stdout);
	n=read()+1,m=read();
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1,l,r;i<=m;i++){
		l=read(),r=read()+1;
		deg[l]++,deg[r]++,add(l,r),add(r,l);
		merge(l,r);
	}
	for(int i=1;i<=n;i++) if(getfa(i)!=getfa(1)) puts("0"),exit(0);
	for(int i=1;i<=n;i++) if(deg[i]==1) q.push(i);
	while(!q.empty()){
		int x=q.front();q.pop();vis[x]=1;
		for(int i=head[x],y;i;i=to[i]) if(!vis[y=ver[i]]) if(--deg[y]==1) q.push(y);
	}
	for(int i=1;i<=n;i++) if(deg[i]==2&&!vis[i]){
		p=0,S=0,T=0;dfs(i);
		if(!T) base=base*p%mod;else e[++pe]=(E){S,T,p+1};
	}
	for(int i=1;i<=n;i++) if(!vis[i]) id[i]=++idx;
	for(int i=2,x,y;i<=tot;i+=2){
		x=ver[i],y=ver[i^1];if(vis[x]||vis[y]) continue;
		x=id[x],y=id[y];
		(a[x][x]+=1)%=mod,(a[y][y]+=1)%=mod;
		(a[x][y]+=mod-1)%=mod,(a[y][x]+=mod-1)%=mod;
	}
	for(int i=1;i<=pe;i++) und=und*e[i].v%mod;
	for(int i=1,x,y,v;i<=pe;i++){
		x=e[i].x,y=e[i].y,v=qpow(e[i].v,mod-2);
		x=id[x],y=id[y];
		(a[x][x]+=v)%=mod,(a[y][y]+=v)%=mod;
		(a[x][y]+=mod-v)%=mod,(a[y][x]+=mod-v)%=mod;
	}
	printf("%lld\n",det(idx-1)*base%mod*und%mod);
	return 0;
}

T3

如果这一列只有一张卡,那么就可以决定所在的行是否需要翻转

如果有两张以上那么就可以求出至多一张正面向上,再将所有的都翻转来得到至少一张正面向上

发现至多一张正面向上的情况可以直接用 \(2-sat\) 来做

该列某卡片正面向上 \(\rightarrow\) 该列其他卡片正面向下,连边时前后缀优化一下

每行一个变量表示是否翻转

因为某些众所周知的原因,退火也能通过,所以没写码

posted @ 2022-07-27 20:30  Max_QAQ  阅读(49)  评论(0编辑  收藏  举报