模拟赛20200222【路径异或值种数,贪心模拟DP,分数逼近→斜率最小值】

7 篇文章 0 订阅
5 篇文章 0 订阅
T1 小b爱旅行:

在这里插入图片描述
在这里插入图片描述

题解:

这题与BZOJ2115的想法差不多,任意找出一棵生成树,从1出发的所有路径都可以表示为一条树链异或上一些环。求出环的线性基,对于每条树链在线性基上跑一遍去重(异或出的最小值相同的就是重复的),数一下不等价的树链条数乘上 2 线 性 基 大 小 2^{线性基大小} 2线就是答案。

如果有加边操作:
因为只用考虑1所在的连通块,所以加入的边如果两个端点都不在1所在的连通块我们就不用管它。
否则,如果两个端点都在1所在的连通块,就加入了一条非树边,那么我们在线性基中插入它对应的环,如果插入成功,就重新将所有树链在线性基中跑一遍去重。最多只会插入成功logW次,所以复杂度是 O ( n l o g 2 W ) O(nlog^2W) O(nlog2W)的。
如果两个端点一个在1所在的连通块,一个不在,这时就dfs一下新的连通块来得到新的连通块带来的树链和环。

Code:

#include<bits/stdc++.h>
#define maxn 200005
#define LL long long
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,m,Q,dep[maxn],X[maxn],Y[maxn],q[maxn],tp,cnt;
LL W[maxn],w[maxn<<1],d[65],f[maxn],S[maxn],ans[maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot=1;
bool del[maxn],flg;
inline void line(int x,int y,LL z){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,w[tot]=z;}
inline void ins(LL x){
	for(int i=60;i>=0;i--) if(x>>i&1){
		if(!d[i]) {d[i]=x,flg=1,cnt++;return;}
		x^=d[i];
	}
}
inline LL calc(LL x){
	for(int i=60;i>=0;i--) if(x>>i&1) x^=d[i];
	return x;
}
void dfs(int u,int ff){
	for(int i=fir[u],v;i;i=nxt[i]) if(i^1^ff){
		if(!dep[v=to[i]]) dep[v]=dep[u]+1,S[++tp]=f[v]=f[u]^w[i],dfs(v,i);
		else ins(f[u]^f[v]^w[i]);
	}
}
struct HahsMap{
	enum {mod = 1000007};
	int fir[mod],nxt[maxn],tot; LL val[maxn];
	void clear(){memset(fir,0,sizeof fir),tot=0;}
	void insert(LL x){
		int u=x%mod;
		for(int i=fir[u];i;i=nxt[i]) if(val[i]==x) return;
		nxt[++tot]=fir[u],fir[u]=tot,val[tot]=x;
	}
}H;
int main()
{
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	read(n),read(m),read(Q);
	for(int i=1;i<=m;i++) read(X[i]),read(Y[i]),read(W[i]);
	for(int i=1;i<=Q;i++) read(q[i]),del[q[i]]=1;
	for(int i=1;i<=m;i++) if(!del[i]) line(X[i],Y[i],W[i]),line(Y[i],X[i],W[i]);
	dep[1]=1,S[tp=1]=0,dfs(1,0);
	while(tp) H.insert(calc(S[tp--]));
	for(int i=Q;i>=1;i--){
		ans[i]=H.tot*(1ll<<cnt),flg=0;
		int x=X[q[i]],y=Y[q[i]]; LL t=W[q[i]];
		if(dep[x]&&dep[y]) ins(f[x]^f[y]^t);
		else{
			line(x,y,t),line(y,x,t); if(!dep[x]) swap(x,y);
			if(dep[x]) dfs(x,0);
		}
		if(!flg) while(tp) H.insert(calc(S[tp--]));
		else{
			H.clear();
			for(int i=1;i<=n;i++) if(dep[i]) H.insert(calc(f[i]));
		}
	}
	printf("%lld\n",H.tot*(1ll<<cnt));
	for(int i=1;i<=Q;i++) printf("%lld\n",ans[i]);
}
T2 小b爱取模

在这里插入图片描述
1 ≤ n ≤ 1 0 7 , 2 ≤ k ≤ 4 1\le n\le10^7,2\le k\le 4 1n107,2k4

题解:

根据常见套路,区间+1差分转化为+1,-1
在这里插入图片描述
可以发现在从左到右扫的过程中要始终保证+1的个数大于等于-1的个数。
下面的题解来自cz_xuyixuan
在这里插入图片描述
妙极。

Code:

#include<bits/stdc++.h>
#define maxn 10000005
using namespace std;
int k,cnt[4],ans,now;
char a[maxn];
int main()
{
	freopen("modulo.in","r",stdin);
	freopen("modulo.out","w",stdout);
	scanf("%d%s",&k,a+1);
	for(int i=1;a[i];i++){
		int x=(a[i]-a[i-1]+k)%k;
		cnt[x]++,now-=x;
		if(now<0) for(int j=k-1;j>=0;j--) if(cnt[j]) {cnt[j]--,ans+=k-j,now+=k;break;}
	}
	printf("%d\n",ans);
}

U p d   20.05.12 Upd~20.05.12 Upd 20.05.12:拓展:
题目改为每次可以区间选择一个区间 + v +v +v ( v v v任取), n ≤ 500 , k = 7 n\le500,k=7 n500,k=7
同样差分,如果大小为 T T T的组的和模 7 7 7余0,那么就可以用 T − 1 T-1 T1次操作将这个组清零。
那么问题变为将 n + 1 n+1 n+1个数分为最多的组,使得每组的和模7余0。
首先贪心,1和6,2和5,3和4首先配对,然后就只剩下3种数,设 F x , y , z , w F_{x,y,z,w} Fx,y,z,w表示用了三种数分别用了 x , y , z x,y,z x,y,z个,最后一组的模为 w w w时最多的组数, O ( 1 ) O(1) O(1)转移DP即可。第一维数组需要滚动。
Code:

#include<bits/stdc++.h>
#define maxn 505
using namespace std;
int n,a[maxn],b[4],c[4],f[2][maxn][maxn][7],cnt[7],ans;
inline void chkmax(int &x,int y){x=max(x,y);}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=n+1;i>=1;i--) cnt[a[i]=(a[i]-a[i-1]+7)%7]++;
	for(int i=1;i<=3;i++)
		if(cnt[i]<cnt[7-i]) b[i]=7-i,c[i]=cnt[7-i]-cnt[i],ans+=cnt[i];
		else b[i]=i,c[i]=cnt[i]-cnt[7-i],ans+=cnt[7-i];
	memset(f[0],-1,sizeof f[0]);
	int now=0,x; f[now][0][0][0]=0;
	for(int i=0;i<=c[1];i++,now=!now){
		memset(f[!now],-1,sizeof f[!now]);
		for(int j=0;j<=c[2];j++)
			for(int k=0;k<=c[3];k++) if(i<c[1]||j<c[2]||k<c[3])
				for(int w=0;w<=6;w++) if(~(x=f[now][j][k][w])){
					if(i<c[1]) chkmax(f[!now][j][k][(w+b[1])%7],x+(w+b[1]==7));
					if(j<c[2]) chkmax(f[now][j+1][k][(w+b[2])%7],x+(w+b[2]==7));
					if(k<c[3]) chkmax(f[now][j][k+1][(w+b[3])%7],x+(w+b[3]==7));
				}
	}
	printf("%d\n",ans+c[1]+c[2]+c[3]-f[!now][c[2]][c[3]][0]);
}
T3:小b爱实数

在这里插入图片描述
1 ≤ n ≤ 1 0 6 , 0 ≤ f ≤ 1 1\le n\le10^6,0\le f\le1 1n106,0f1

题解:

在这里插入图片描述
我一开始的想法是两点间斜率最小可以分别维护一个上凸包和下凸包,对 i i i求求斜率负方向靠近0时就在下凸包上二分找到 y j ≥ y i y_j\ge y_i yjyi的最大的 j j j,正方向同理。

Code:

#include<bits/stdc++.h>
#define maxn 1000005
#define gety(i) (s[i]-(i)*f)
using namespace std;
const double eps = 1e-8;
int n,L,id[maxn],s[maxn];
double f,ans=1e20;
char a[maxn];
bool cmp(int i,int j){return gety(i)<gety(j);}
int main()
{
	freopen("real.in","r",stdin);
	freopen("real.out","w",stdout);
	scanf("%lf%s",&f,a+1),n=strlen(a+1);
	for(int i=1;i<=n;i++) s[i]=s[i-1]+(a[i]=='1'),id[i]=i;
	sort(id,id+1+n,cmp);
	for(int k=1;k<=n;k++){
		int i=id[k],j=id[k-1];
		double t=fabs((gety(i)-gety(j))/(i-j));
		if(t<ans-eps) ans=t,L=min(i,j);
		else if(t<ans+eps) L=min(L,min(i,j));
	}
	printf("%d\n",L);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值