Codeforces Round #639 (Div. 2)划水記

文章目錄

unratedsolve just for fun?好不爽\rightarrow unrated\rightarrow solve~ just~ for~ fun?

AA

題意:拼圖,每塊拼圖部件有一個凹三個凸,只有凹凸可以貼合.問能否拼成nmn*m的圖.

每個點有1個出度,3個入度.(邊界的點看作對外有出度)

那麼n=1m=1n=1\cup m=1 一定可以.

n=m=2n=m=2,由樣例得可行.

對於其他情況一定包含一個23322*3或3*2的矩陣.

而這個矩陣內有7個相接的地方,由於每個相接的地方都有有凹,但實際上6<7,所以無論如何都不行.

int T,n,m; 

int main() {
	qr(T); while(T--) {
		qr(n); qr(m);
		if(n==m&&n==2) puts("yes");
		else if(min(n,m)==1) puts("yes");
		else puts("no");
	}
	return 0;
}

BB

容易看出一個DP轉移函數:

f[i]f[i]表示高度爲ii要多少張牌.

{f[0]=0,f[1]=1f[i]=2f[i1]f[i2]+3(i>1)\begin{cases}f[0]=0,f[1]=1\\f[i]=2f[i-1]-f[i-2]+3(i>1)\end{cases}

打表找到上界ii,然後每次用二分來查數即可.

int T,n,ans;
ll f[N];

void dfs(int x) {
	while(x>=2) {
		x-=(*(upper_bound(f+1,f+N,x)-1));
		ans++;
	}
}

int main() {
	f[1]=2;for(int i=2;i<N;i++) f[i]=2*f[i-1]-f[i-2]+3;
	qr(T);while(T--) {
		ans=0; qr(n);
		dfs(n); pr2(ans);
	}
	return 0;
}

更快的做法:

可以發現f[n]=1.5n2+0.5nf[n]=1.5*n^2+0.5n

解方程即可.

CC

一開始題目看不懂,QAQ…

題意:給你nn個整數a0,a1....an1a_0,a_1....a_{n-1},判斷是否不存在iji+a[imod  n]=j+a[jmod  n]i\ne j且i+a[i\mod n]=j+a[j\mod n] .

首先對於一個同餘系的數一定不會衝突.

然後我們枚舉i,j(0i<j<n)i,j(0\le i<j<n),判斷是否

i+a[i]j+a[j](mod  n)i+a[i]\equiv j+a[j](\mod n)

開個bool數組判斷(i+a[i])mod  n(i+a[i])\mod n是否互不相同即可.

int T,n,a[N],ans,vis[N],num; 

int main() {
	qr(T); while(T--) {
		qr(n);for(int i=0;i<n;i++) qr(a[i]);
		ans=0; num++;
		for(int i=0,x;i<n;i++) {
			x=(i+a[i]%n+n)%n;
			if(vis[x]==num) {puts("No");ans=1;break;}
			else vis[x]=num;
		}
		if(!ans) puts("YES");
	}
	return 0;
}

DD

題意:

給定nmn*m的顏色(黑白)矩陣.開始隨意放置S/N極.

之後當一對S,N極共享一個行/列,且不在同一個位置時,N往S靠近一個單位.

這個初始放置必須在隨意移動後滿足:

  • S極出現在每一行,每一列.
  • N極可以到達所有的黑格.
  • N極無法到達所有的白格.

求初始放置的最少N極數.

引理:

  • 每行/列只能有一段黑.
  • 一行無黑必須保證有列也無黑.

判定完不合法的情況後,只需計算黑色連通塊數即可.

int n,m,v[N][N],cnt;
bool r[N],c[N];
char s[N][N];

void out(){puts("-1"),exit(0);}

const int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
void dfs(int x,int y) {
	v[x][y]=cnt;
	for(int t=0;t<4;t++) {
		int tx=x+dx[t],ty=y+dy[t];
		if(s[tx][ty]=='#'&&!v[tx][ty]) dfs(tx,ty);
	}
} 

int main() {
	qr(n);qr(m); 
	//每行/列至多有一段黑. 
	for(int i=1;i<=n;i++) {
		scanf("%s",s[i]+1);
		for(int j=1,k=0;j<=m;j++) 
			if(s[i][j]=='#') {
				r[i]=c[j]=1;
				if(s[i][j]!=s[i][j-1]) {
					if(++k>=2) out();
				}
			}
	}
	for(int j=1;j<=m;j++) 
		for(int i=1,k=0;i<=n;i++) 
			if(s[i][j]=='#') {
				r[i]=c[j]=1;
				if(s[i][j]!=s[i-1][j]) {
					if(++k>=2) out();
				}
			}
	//每行每列都要有S極 
	int a=0,b=0;
	for(int i=1;i<=n;i++) a+=!r[i];
	for(int j=1;j<=m;j++) b+=!c[j];
	if(a^b&&(!a||!b)) out();
	//接下來,每個黑色連通塊都放一個N極. 
	for(int i=1;i<=n;i++) 
		for(int j=1;j<=m;j++) if(s[i][j]=='#'&&!v[i][j]) cnt++,dfs(i,j);
	pr2(cnt); 
	return 0;
}

實際可用並查集來合併行列(因爲每行/列至多有一段).

EE

題目好繞,對我很不友好.

:nx1,x2...xn,m(xaj<xbj,j[1,m]),簡明題意:有n個變量x_1,x_2...x_n,m條相互關係(x_{a_j}<x_{b_j},j\in[1,m]),設f(x)=j=1mxaj<xbj(and)),Q1x1,Q2x2..Qnxnf(x)=true,Qi=/f(x)=\cap_{j=1}^m x_{a_j}<x_{b_j}(\cap指的是and運算)),最後的令Q1x_1,Q2x_2..Qnx_n f(x)=true,Qi=\forall /\exist

最後,求滿足條件的最多的\forall數量

我們把相互關係看作(aj,bj)(a_j,b_j),即一條有向邊.

則顯然,如果成環則無論如何都不合法,輸出1.(x<x)-1.(x<x)

然後,任意兩個有大小關係的數至多有一個\forall.

所以我們用拓撲判環+正反向DP即可.

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define gc getchar()
using namespace std;
const int N=2e5+10; 

void qr(int &x) {
	char c=gc; x=0;
	while(!isdigit(c))c=gc;
	while(isdigit(c))x=x*10+c-'0',c=gc;
}

int n,m,f[N],g[N],ans,q[N];
struct edge{int y,next;}a[N]; int len,last[N],deg[N];
void ins(int x,int y) {a[++len]=(edge){y,last[x]};last[x]=len;deg[y]++;}

void topsort() {
	int l=1,r=0;
	for(int i=1;i<=n;i++) if(!deg[i]) q[++r]=i;
	while(l<=r) {
		int x=q[l++];
		for(int k=last[x],y;k;k=a[k].next) {
			y=a[k].y;
			f[y]=min(f[x],f[y]);
			if(!(--deg[y])) q[++r]=y;
		}
	}
	if(r<n) puts("-1"),exit(0);
	while(r) {
		int x=q[r--];
		for(int k=last[x],y;k;k=a[k].next) {
			y=a[k].y;
			g[x]=min(g[x],g[y]);
		}
	}
}

int main() {
	qr(n); qr(m);
	for(int i=1;i<=n;i++) f[i]=g[i]=i;
	for(int i=1,x,y;i<=m;i++) {
		qr(x),qr(y);
		ins(x,y);
	}
	topsort();
	for(int i=1;i<=n;i++) f[i]=min(f[i],g[i]),ans+=(f[i]==i);
	printf("%d\n",ans);
	for(int i=1;i<=n;i++) putchar("EA"[f[i]==i]);
	return 0;
}
//有環一定不合法.
//然後對於任意有大小關係的變量之間只能有一個forall.
//於是,對每個點求跟其大小相關的點的最小值. 
//做法:用拓撲排序判環/正反向DP. 

FF

定義二元函數f(x,y)=x(yx2)f(x,y)=x(y-x^2)

我們考慮增量法:把x-1弄到x->Δ=f(x,a[i])f(x1,a[i])=a13x(x1)\Delta =f(x,a[i])-f(x-1,a[i])=a-1-3x(x-1).

顯然這是一個當x1x\ge 1時遞減的函數.

一個貪心的做法是每次取最大的變化量,複雜度O(kn)O(kn),配合堆優化可O(klogn)O(k\log n)

但是k太大了,不能直接處理.

我們要利用到增量的遞減性質.

由貪心思路得:每次選擇的變化量不嚴格遞減.

所以,如果我們確定最後一個變化量,即可求出最多變化多少次.這個考慮二分處理:l=mini=1nf(a[i],a[i]),r=maxi=1nf(1,a[i])l=\min_{i=1}^n f(a[i],a[i]),r=\max_{i=1}^n f(1,a[i])

最後還剩下至多nn個數可選擇,對還能選擇的量進行排序貪心選大的即可.

ps:對於每個aia_i,求最多增加幾次的時候可二分可用解二次方程的方法

複雜度:O(nlog(rl)logmaxai)O(n\log (r-l)\log maxai)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<functional>
#define pi pair<ll,int>
#define ll long long
using namespace std;
const int N=1e5+10; 

int n,b[N];
ll m,a[N],tot;
pi s[N];int cnt;

ll f(ll x,ll y) {return y-1-3*x*(x-1);}
int calc(ll x,ll t) {//x爲a[i],t爲delta的下界 .找第一個<t的位置 
	int l=1,r=x+1,mid;
	while(l<r) {
		mid=l+r>>1;
		if(f(mid,x)<t) r=mid;
		else l=mid+1;
	}
	return l-1;
}
bool check(ll x) {
	tot=0;
	for(int i=1;i<=n&&tot<=m;i++) tot+=(b[i]=calc(a[i],x));
	return tot<=m;
}

int main() {
	scanf("%d %lld",&n,&m);
	ll l=9e18,r=0,mid;
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]),r=max(r,f(1,a[i])),l=min(l,f(a[i],a[i]));
	while(l<r) {
		mid=l+r>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	check(l); 
	m=m-tot;
	if(m) for(int i=1;i<=n;i++) 
		if(b[i]<a[i])
			s[++cnt]=make_pair(f(b[i]+1,a[i]),i);
	sort(s+1,s+cnt+1,greater<pi>());
	for(int i=1;i<=m;i++) b[s[i].second]++;
	for(int i=1;i<=n;i++) printf("%d ",b[i]);
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章