5月27日

5月27日

I'm always here if you need me.

前传

但是 There is no time you need me.

三个人铃声响完挨着准备进信息教室,结果两个人进去之后,Mr俞把门关了,于是樊子奆就只能敲门了,关门3s后门开了,于是门上课时一直就没关。


大扫除:

卢宇宸准备去拿林烨手里的报纸:

“不行!我还要看!”

林烨可可爱爱,班长一脸无奈。

季亦贝:同学们让一让,人家要擦空调了。

林烨:对要擦扇贝。

我觉得我可以进化成磕cp的人。


“是1/2吧?你们不要不说话啊,我还以为我错了呢”

“也不要当我是一点数学也不会啊”


历史课下,眼保健操铃响了,殷准备去关铃。章云皓小朋友明目张胆skip out of the front door。

“太过分了”前一秒

“你们是不是要查操啊?那快去啊”后一秒


和云再一次达成共识——单身快乐

“还有没有什么要吐槽的?"

"蔡蔡呢"

“蔡蔡很好,蔡蔡没什么要吐槽的”

“刘丽钧呢?”

“小姑娘挺可爱的,就是做操不太认真,今天早上被杜林存骂了,骂的可狠了。

CF1517E Group Photo

2500开场,美好的一天。细节好烦啊,烦的我自闭了不过完完整整做了道题

大概发现,一定是一段连着的,然后空一格的,最后一个(可以没有)和前面隔很远,于是就有了分类一下前后缀种类,就有了以下五种情况,这是暴力。

		for (int i=n;i>=0 && pre[i]>suf[i+1];i--) ans++; 
		for (int i=1;i<=n;i++)
			for (int j=n;j>i;j--){
				if ((j-i-1)&1) continue;
				if (i>=2 && j<n) ans+=chk(i+1,j-1,suf[j]-2*a[n]-pre[i]+2*a[1]);//PCCC...PCPC...PPPC
				if (i>=2) ans+=chk(i+1,j-1,suf[j]-pre[i]+2*a[1]);//PCCc...PPPP
				if (j<n) ans+=chk(i+1,j-1,suf[j]-2*a[n]-pre[i]);//CCCC...PPPC
				ans+=chk(i+1,j-1,suf[j]-pre[i]); //CCC...PPP
			}

然后发现因为随着j的后移,sumc会增大,所以i后移时,j一定是前移的,也就是有单调性。

所以就可以分奇偶四种情况分别做一下就好了。

#include <bits/stdc++.h>
typedef long long ll;
const int N=200005,mu=998244353;
int T,n,a[N];
ll s[N],ans,pre[N],suf[N];
int chk(int l,int r,ll sum){
	sum+=s[r]-s[l-1];
	return sum>0;
}
void solve(int L,int R,int tg1,int tg2){
	for (int i=L,j=R;i<=n;i+=2){
		ll s1=pre[i];
		if (tg1) s1-=2*a[1];
		for (;;j--){
			if (j<=i) return;
			if ((j-1-i)&1) continue;
			ll s2=suf[j];
			if (tg2) s2-=2*a[n];
			if (chk(i+1,j-1,s2-s1)) break;
		} 
		ans+=(j-i+1)/2;
	}		
}
int main(){
	scanf("%d",&T);
	while (T--){
		ans=0;
		scanf("%d",&n);
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		for (int i=1;i<=n;i++){
			if (i>2) s[i]=s[i-2]-a[i]+a[i-1]; 
			pre[i]=pre[i-1]+a[i];
		}
		suf[n+1]=0;
		for (int i=n;i>=1;i--) suf[i]=suf[i+1]+a[i];
		//pppppcccc
		for (int i=n;i>=0 && pre[i]>suf[i+1];i--) ans++; 
		solve(2,n-1,1,1);
		solve(3,n-1,1,1);
		solve(2,n,1,0);
		solve(3,n,1,0);
		solve(1,n-1,0,1);
		solve(2,n-1,0,1);
		solve(1,n,0,0);
		solve(2,n,0,0);
		printf("%lld\n",ans%mu);
	}
} 

决策单调性,思维,细节

CF1510B Button Lock

d很小,可以暴力建一张图,每个点向它的子集连边。

像最小链覆盖一样,但是这里我们不止要考虑链的条数,还要考虑最多串中1的个数。

大概就是先假装所有链顶无父亲,假装源点是父亲,要暴力构造清零 \((S,x',|s_x+1|)\) ,然后左边一列出点,右边一列入点,\((x,y',0)\) 表示把 \(y\) 继承于 \(x\) 后可以省下的,\((S,x,1,0)\),\((x',T,1,0)\) 保证每个点都考虑到,最小费用最大流即可

好像就做完了,输出方案再做一做就好了。

#include <bits/stdc++.h>
const int M=70005,N=2205,INF=1e9;
char s[N][10];
int n,m,edge,last[N],Next[M<<1],w[M<<1],to[M<<1],d[N],cnt,match[N];
int dis[N],vis[N],v[M<<1],flow[N],S,T,a[N],f[N],id[N][N];
std::queue<int> q;
void add(int x,int y,int ww,int zz){
	//printf("%d %d %d %d\n",x,y,ww,zz);
	to[++edge]=y;
	Next[edge]=last[x];
	last[x]=edge;
	w[edge]=ww;
	v[edge]=zz;
}
int c(int x){
	return (x&1)?x+1:x-1;
}
void Add(int x,int y,int ww,int zz){
	add(x,y,ww,zz);
	add(y,x,0,-zz);
}
bool chk(int x,int y){
	for (int i=0;i<m;i++)
		if (s[y][i]=='1' && s[x][i]=='0') return 0;
	return 1;
}
bool SPFA(){
	memset(dis,0x3f,sizeof(dis));
	memset(flow,0x3f,sizeof(flow));
	dis[S]=0;
	q.push(S);
	vis[S]=1;
	while (q.size()){
		int x=q.front();
		q.pop();
		vis[x]=0;
		for (int i=last[x];i;i=Next[i]){
			int u=to[i];
			if ((!w[i]) || dis[x]+v[i]>=dis[u]) continue;
			dis[u]=dis[x]+v[i];
			f[u]=i;
			flow[u]=std::min(flow[x],w[i]);
			if (!vis[u]) q.push(u),vis[u]=1;
		}
	}
	return dis[T]<INF;
}
struct Edge{
	int x,y,id;
}e[M];
int main(){
	scanf("%d%d",&m,&n);
	for (int i=1;i<=n;i++){
		scanf("%s",s[i]);
		int cnt=0;
		for (int j=0;j<m;j++)
			if (s[i][j]=='1') cnt++;
		a[i]=cnt;
	}
	S=2*n+1,T=S+1;
	for (int i=1;i<=n;i++) Add(S,i,1,0),Add(S,i+n,1,a[i]+1),Add(i+n,T,1,0);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++){
			if (i==j) continue;
			if (chk(i,j)) Add(i,j+n,1,0),e[++cnt]={i,j,edge}; 
		}
	int cost=-1;
	while (SPFA()){
		cost+=dis[T]*flow[T];
		int u=T;
		while (u!=S){
			w[f[u]]-=flow[T];
			w[c(f[u])]+=flow[T];
			u=to[c(f[u])];
		}
	}
	printf("%d\n",cost);
	for (int i=1;i<=cnt;i++)
		if (w[e[i].id]){
			match[e[i].y]=e[i].x;
			d[e[i].x]++;
		}
	int tg=0;
	for (int i=1;i<=n;i++){
		if (d[i]) continue;
		if (tg) printf("%c ",'R');
		tg=1;
		int u=i;
		for (int j=0;j<m;j++) if (s[u][j]=='1') printf("%d ",j);
		while (match[u]){
			int v=match[u];
			for (int j=0;j<m;j++)
				if (s[v][j]=='1' && s[u][j]=='0') printf("%d ",j);
			u=v;
		}
	}
}

网络流

逐渐因懒于看题解而学会自己做题

CF1521E Nastia and a Beautiful Matrix

想了一下。

下界,肯定就是四个都填三个,角的地方顶上。

上界,直接一行空一行,答案一定是 \(\sqrt n\) 级别的。

有一种感觉,是先隔行填了,然后,选不相同的填上去,只要不冲突即可达到下界。那么一定是先众数全填,别的数填完,然后去补众数下面的,也就是如果众数 \(x \le 2(n-x)\) 那么就做完了。

有点问题,其他数填到最后会崩。那就不要隔行,先四个格子填一个,填满一遍在每个格子填第二个,就行了,这样就不会崩了。

如果 大于 的话。那也做完了?每四个格子最多两个众数,众数填完别的随便填就好了。

想不清楚 我们来找一个短一点的 题解 瞧一瞧

查了半天错回去发现掉落re...原来数组开小了

#include <bits/stdc++.h>
int T,n,m,a[100005];
int c[600][600],ans;
void solve(int t1,int t2,int x,int &mx){//填众数,尽量使接下来的数不会冲突,也就是剩下非对角线
	if (!mx) return;
	for (int i=t1;i<=ans;i+=2)
		for (int j=t2;j<=ans;j+=2){
			c[i][j]=x,mx--;	
			if (!mx) return;
		}
}
void solve2(int t1,int t2,int &p){//按不同格依次填,保证不会冲突
	for (int i=t1;i<=ans;i+=2)
		for (int j=t2;j<=ans;j+=2){
			if (c[i][j]) continue;
			while (p<=m && (!a[p])) p++;
			if (p>m) return;
			c[i][j]=p;
			a[p]--;
		}
}
int main(){
	scanf("%d",&T);
	while (T--){
		scanf("%d%d",&n,&m);
		int mx=0,x=0;
		for (int i=1;i<=m;i++){
			scanf("%d",&a[i]);
			if (a[i]>mx) mx=a[i],x=i; 
		}
		int L=(int)sqrt(n),R=(int)sqrt(n*2)+1;
		while (L<=R){
			int mid=(L+R)>>1,s,s2;
			if (mid&1) s=(mid-1)*(mid-1)/4*3+mid+mid-1;
			else s=mid*mid/4*3;
			s2=((mid+1)/2)*mid;
			if (s>=n && s2>=mx) ans=mid,R=mid-1;
			else L=mid+1; 
		}
		printf("%d\n",ans);
		for (int i=1;i<=ans;i++)
			for (int j=1;j<=ans;j++) c[i][j]=0;
		solve(1,2,x,mx);
		solve(1,1,x,mx);
		a[x]=0;
		int p=1;
		solve2(1,2,p);
		solve2(1,1,p);
		solve2(2,1,p);
		for (int i=1;i<=ans;i++,puts(""))
			for (int j=1;j<=ans;j++) printf("%d ",c[i][j]);
	}
}

CF1442D Sum

2800冲冲冲!

PA: 2800以上的就不要去做了。于是每日计划2500+2600+2700+2800 的快乐生活开始了。

堆?凸包?背包?...题解!

我们来思考一个问题,如果你当前选了一个你不想选的,那一定是因为后面有一个值得选的,那如果后面值得选了,那因为不降,后面那一连串都值得,感性理解,一个数组会被全选完。最多会有一个因 \(k\) 的限制而无法选到。还是题解比较理性一点。

问题转化,对于全选的数组,一定按总和与个数的比择优选择。

所以枚举最后那个数组,把别的排个序,扫一遍取前多少个?

发现他wa了,感觉这个思路不太行,需要用到背包,又是一个缺一背包,可以用分治来做。

但是开始想一个问题,如果这个东西没有选完,那排序后比他小的还会被完全选吗?当然会,所以乖乖地明天早上来写分治吧!冲题量失败。

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=3005;
int n,k,siz[N],cnt;
ll dp[N],ans,tmp[20][N];
vector<int> a[N]; 
struct point{
	ll x;
	int v;
}b[N];
void upd(int x,ll y){
	for (int i=k;i>=x;i--)
		dp[i]=std::max(dp[i-x]+y,dp[i]);
}
void solve(int L,int R){
	if (L==R){
		ll sum=0;
		for (int i=1;i<=siz[L] && i<=k;i++){
			sum=sum+a[L][i-1];
			ans=std::max(ans,sum+dp[k-i]);
        }
		return;
	}
	int now=++cnt;
	memcpy(tmp[now],dp,sizeof(dp));
	int mid=(L+R)>>1; 
	for (int i=mid+1;i<=R;i++)
		upd(b[i].v,b[i].x);
	solve(L,mid);
	memcpy(dp,tmp[now],sizeof(dp));
	for (int i=L;i<=mid;i++)
		upd(b[i].v,b[i].x);
	solve(mid+1,R);
	memcpy(dp,tmp[now],sizeof(dp));
	cnt--; 
}
int main(){
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++){
		ll sum=0;
		scanf("%d",&siz[i]);
		a[i].resize(siz[i]);
		for (int j=0;j<siz[i];j++)
			scanf("%d",&a[i][j]),sum+=a[i][j];
		b[i]={sum,siz[i]};
	}
	solve(1,n);
	printf("%lld\n",ans);
}

分治,dp,结论

1-mid的悲剧....

posted @ 2021-05-28 10:32  flyfeather  阅读(179)  评论(0编辑  收藏  举报