codeforces 上的小题(脑筋急转弯)

CF1305C   

题解:   

我们发现虽然 $n$ 很大,但是模数很小,所以相当于 $n$ 个数对 $m$ 取模后不能有重复数字.   

那么其实这个 $n$ 最大也就是 $m$ ,直接 $O(m^2)$ 暴力算就行了. 

code: 

#include <bits/stdc++.h>   
#define ll long long  
#define N 2004  
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;    
int vis[N],arr[200007],bu[200007];    
int main() 
{ 
	// setIO("input");  
	int i,j,n,mod;
	scanf("%d%d",&n,&mod);          
	for(i=1;i<=n;++i) 
	{   
		scanf("%d",&arr[i]);  
		bu[i]=arr[i];   
		arr[i]%=mod;    
		if(vis[arr[i]]) 
		{
			printf("0
"); 
			return 0;      
		}
		vis[arr[i]]=1;       
	}        
	int ans=1;
	int tmp=1;      
	for(i=1;i<=n;++i) 
	{
		for(j=i+1;j<=n;++j) 
		{
			if(bu[i]<bu[j]) 
				tmp*=-1;  
			ans=ans*(arr[i]-arr[j]+mod)%mod;   
		}
	}    
	printf("%d
",(tmp*ans+mod)%mod);  
	return 0;
}

  

CF1310A 

我们发现最优的操作方式一定是从小到大,价格高到价格低来进行操作的.   

而我们每增加 1,就会少一个数,所以只需维护当前数中代价最大的就行.   

然后当碰到下一个数的时候来一个堆的启发式合并就行了. 

code: 

#include <bits/stdc++.h>   
#define N 200007
#define ll long long    
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;  
int rk[N],a[N],t[N],A[N],id[N];     
map<int,int>mp;  
priority_queue<int>q[N];
ll sum[N];     
int main() 
{ 
	// setIO("input");     
	int i,j,n;        
	scanf("%d",&n);   
	for(i=1;i<=n;++i)   
		scanf("%d",&a[i]),A[i]=a[i],mp[a[i]]=1;     
	for(i=1;i<=n;++i) 
		scanf("%d",&t[i]);       
	sort(A+1,A+1+n); 
	int tmp=unique(A+1,A+1+n)-A-1;            
	for(i=1;i<=n;++i)   
		rk[i]=lower_bound(A+1,A+1+tmp,a[i])-A;                    
	for(i=1;i<=n;++i)     
	{
		q[rk[i]].push(t[i]);         
		sum[rk[i]]+=t[i];  
	} 
	ll ans=0;  
	for(i=1;i<=tmp;++i)  
		id[i]=i;  
	for(i=1;i<=tmp;++i)   
	{
		int cur=A[i];     
		for(j=cur;(j==cur||!mp[j])&&!q[id[i]].empty();++j) 
		{   
			int u=q[id[i]].top(); q[id[i]].pop();       
			ans+=sum[i]-u;       
			sum[i]-=u;    
		}  
		if(!q[id[i]].empty()) 
		{       
			if(q[id[i+1]].size()<q[id[i]].size()) 
			{           
				while(!q[id[i+1]].empty()) 
				{
					q[id[i]].push(q[id[i+1]].top());   
					q[id[i+1]].pop();      
				}
				id[i+1]=id[i];  
			} 
			else
			{
				while(!q[id[i]].empty()) 
				{
					q[id[i+1]].push(q[id[i]].top());   
					q[id[i]].pop();      
				}    
			}
			sum[i+1]+=sum[i];    
		}
	}
	printf("%lld
",ans);  
	return 0;   
}

  

  

CF1316C

题解:给定两个多项式,求相乘后第几项不会被质数 $p$ 整除.   

直接硬推的话是推不出来的,因为多项式相乘是卷积运算.   

但是假如说我们能在两个多项式中分别找到第一项不能被 $p$ 整除的 $i,j$ 的话第 $(i+j)$ 项就一定是答案了.   

至于正确性,手画一下多项式卷积的情况就可以得知,在 $(i+j)$ 项的系数模 $p$ 后只有 $a[i] imes b[j]$ 是有贡献的.  

由于题目保证了 $gcd(a[0],a[1],....a[n])=gcd(b[0],b[1],....b[m])=1$,故一定能找到这样的 $i,j$.  

code: 

#include <bits/stdc++.h>   
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
int main() 
{ 
	// setIO("input");   
	int i,j,n,m,mod,a1=-1,a2=-1;  
	scanf("%d%d%d",&n,&m,&mod);       
	for(i=0;i<n;++i) 
	{
		int x;   
		scanf("%d",&x);   
		if((x%mod)&&a1==-1) 
			a1=i;   
	}
	for(i=0;i<m;++i) 
	{
		int x; 
		scanf("%d",&x);  
		if((x%mod)&&a2==-1) 
			a2=i;  
	}
	printf("%d
",a1+a2);  
	return 0; 
}

  

  

CF1320C 

题解:我们可以一维枚举,另一位用线段树维护(扫描线). 这个套路挺常见的. 

code: 

#include <bits/stdc++.h>  
#define ll long long 
#define N 200007  
#define lson now<<1 
#define rson now<<1|1     
#define MAX 1000000   
#define inf 10000000000000000     
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;        
int A[MAX+2],B[MAX+2];   
struct node {
	int x,y,z;   
}no[N];               
struct data {       
	ll tag,maxx;      
}s[(MAX+1)<<2];          
bool cmp(node a,node b) {  
	return a.x<b.x; 
}     
inline void mark(int now,ll v) 
{
	s[now].maxx+=v;    
	s[now].tag+=v;   
}
inline void pushdown(int now) 
{
	if(s[now].tag) 
	{
		mark(lson,s[now].tag); 
		mark(rson,s[now].tag);  
		s[now].tag=0;  
	}
}
void build(int l,int r,int now) 
{
	if(l==r) 
	{ 
		s[now].maxx=(ll)B[l]?(ll)-B[l]:-inf;    
		return ; 
	}   
	int mid=(l+r)>>1;   
	build(l,mid,lson),build(mid+1,r,rson);    
	s[now].maxx=max(s[lson].maxx,s[rson].maxx);  
}
void update(int l,int r,int now,int L,int R,int v) 
{
	if(l>=L&&r<=R) 
	{
		mark(now,v);  
		return ; 
	}
	pushdown(now);   
	int mid=(l+r)>>1;   
	if(L<=mid)     
		update(l,mid,lson,L,R,v);     
	if(R>mid)  
		update(mid+1,r,rson,L,R,v);   
	s[now].maxx=max(s[lson].maxx,s[rson].maxx);  
}   
int main() 
{ 
	// setIO("input");   
	int i,j,n,m,p;      
	scanf("%d%d%d",&n,&m,&p);    
	for(i=1;i<=n;++i) 
	{
		int x,y;  
		scanf("%d%d",&x,&y);  
		if(!A[x]||A[x]>y) 
			A[x]=y;  
	}   
	for(i=1;i<=m;++i) 
	{
		int x,y; 
		scanf("%d%d",&x,&y);   
		if(!B[x]||B[x]>y) 
			B[x]=y;       
	} 
	for(i=1;i<=p;++i) 
		scanf("%d%d%d",&no[i].x,&no[i].y,&no[i].z);               
	sort(no+1,no+1+p,cmp);         
	build(1,MAX,1);                    
	ll ans=-inf;  
	for(i=j=1;i<=MAX;++i) 
	{                     
		if(A[i]) 
		{             
			for(;j<=p&&no[j].x<i;++j) 
				if(no[j].y<MAX)
					update(1,MAX,1,no[j].y+1,MAX,no[j].z);       
			ans=max(ans,(ll)-A[i]+s[1].maxx);  
		}
	}
	printf("%lld
",ans);   
	return 0;
}

  

  

CF1321C

题解:手画一下发现这个东西是不能 $DP$ 的(因为有后效性).   

所以们为了满足没有后效性,就先删字符大的,后删字符小的,整个过程用链表维护即可. 

code: 

#include <bits/stdc++.h>   
#define N 205  
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std;    
int n;     
char str[N];   
int a[N],pre[N],nxt[N],vis[N];  
int main() 
{
	// setIO("input");  
	int i,j;         
	scanf("%d%s",&n,str+1);   
	a[0]=a[n+1]=-1;   
	for(i=1;i<=n;++i)   
		a[i]=str[i]-'a',pre[i]=i-1,nxt[i]=i+1;    
	int ans=0;    
	for(i=25;i;--i) 
	{     
		for(int cnt=1;cnt<=100;++cnt) 
		{
			for(j=1;j<=n;++j) 
			{     
				if(!vis[j]&&a[j]==i&&(a[pre[j]]==i-1||a[nxt[j]]==i-1))  
				{
					vis[j]=1;   
					++ans;    
					nxt[pre[j]]=nxt[j];   
					pre[nxt[j]]=pre[j];   
				}
			}
		}
	}    
	printf("%d
",ans);  
	return 0;
}

  

CF1313C2

题解:我们发现我们选的数一定是以一个位置为最高点,向左递减,向右递减这样的形式.  

所以我们只需维护出 $L[i],R[i]$ 表示以 $i$ 点为最高点,向左延申的最大值及向右延伸的最大值.   

然后这个歌东西显然用单调栈求一下就行了. 

code: 

#include <bits/stdc++.h>   
#define ll long long  
#define N 500007  
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;  
stack<int>S;  
int a[N],n;      
int tl[N],tr[N];   
ll L[N],R[N];    
int flag;  
void print_l(int x) 
{
	if(!x) 
		return;       
	print_l(tl[x]);   
	for(int i=tl[x]+1;i<=x;++i)   
		printf("%d ",a[x]);    
}      
void print_r(int x) 
{
	if(x>n)   
		return;   
	for(int i=flag?x:x+1;i<tr[x];++i)   
		printf("%d ",a[x]);   
	flag=1;   
	print_r(tr[x]);  
}
int main() 
{ 
	// setIO("input");  
	int i,j;   
	scanf("%d",&n); 
	for(i=1;i<=n;++i) 
		scanf("%d",&a[i]);         
	S.push(0);   
	for(i=1;i<=n;++i)            
	{        
		while(a[S.top()]>a[i]) 
			S.pop();     
		tl[i]=S.top();       
		L[i]=(ll)(i-S.top())*a[i]+L[S.top()];     
		S.push(i);   
	}    
	S.push(n+1);   
	for(i=n;i>=1;--i)     
	{
		while(a[S.top()]>a[i]) 
			S.pop();   
		tr[i]=S.top();   
		R[i]=(ll)(S.top()-i)*a[i]+R[S.top()];  
		S.push(i);  
	}        
	ll ans=0;  
	int p=0;   
	for(i=1;i<=n;++i)     
		if(L[i]+R[i]-a[i]>ans) 
			ans=L[i]+R[i]-a[i],p=i;      
	print_l(p);
	print_r(p);     
	// printf("%lld
",ans);  
	return 0;
}

  

CF1307C

题解:冷静分析后,我们发现对答案有贡献的其实就是一个单字符或者两个字符.                 

这样的话一共就只有 $26 imes 26$ 种可能了,枚举一下就行了. 

code: 

#include <bits/stdc++.h>   
#define N 100006  
#define ll long long  
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;   
char str[N];   
int dp[26],cn[26]; 
ll t[26][26];  
int main() 
{  
	// setIO("input");  
	int i,j,n; 
	scanf("%s",str+1),n=strlen(str+1);   
	for(i=1;i<=n;++i)   
		++cn[str[i]-'a'];   
	ll ans=0;   
	for(i=0;i<26;++i)   
		ans=max(ans,(ll)cn[i]);   
	for(i=1;i<=n;++i)   
	{    
		dp[str[i]-'a']++;     
		int a=str[i]-'a',b;   
		for(b=0;b<26;++b)   
			t[a][b]+=(ll)cn[b]-dp[b];   
	}
	for(i=0;i<26;++i) 
		for(j=0;j<26;++j)   
			ans=max(ans,t[i][j]);   
	printf("%lld
",ans);   
	return 0;
}

  

CF1304C

题解:我们可以维护每个时刻合法的温度范围 $[L,R]$.  

那么 $d$ 时刻后合法的区间就变为 $[L-d,R+d]$,而如果一个时刻对温度范围有要求的话就是和 $[L,R]$ 的交集. 

模拟即可.  

code:

#include <bits/stdc++.h>   
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;     
void solve() 
{
	int i,j,n,m,cur=0,L,R,flag=0;  
	scanf("%d%d",&n,&m);   
	L=R=m;  
	for(i=1;i<=n;++i) 
	{   
		int t,l,r; 
		scanf("%d%d%d",&t,&l,&r);  
		if(flag)  
			continue;      
		L-=(t-cur);   
		R+=(t-cur);          
		if(R<l||L>r)    
			flag=1;     
		L=max(L,l);  
		R=min(R,r);    
		cur=t;  
	}   
	printf("%s
",flag?"NO":"YES");  
}
int main() 
{ 
	// setIO("input"); 
	int i,j,T; 
	scanf("%d",&T);    
	while(T--) 
		solve();      
	return 0;
}

  

CF1304D

我们发现给定相邻两项的大小关系后这个序列会呈现一个波浪的形式.  

那么先考虑 LIS 的最大值:  

这个值最大就是所以上升段大小之和.   

那么考虑这么构造:      

先让序列等于 $1,2,3,....n$  

维护连续下降段,然后将这段序列翻转,这样可以保证下一个上升段的第二个元素大于当前波峰,能取到最优解.    

LIS 最小值的话就反着构造就行.     

code: 

#include <bits/stdc++.h>        
#define N 200006   
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std;      
char s[N];  
int t,n,a[N];   
void solve() 
{
	int i,j;   
	scanf("%d%s",&n,s+1);  
	for(i=1;i<=n;++i)   
		a[i]=n-i+1;     
	for(i=1;i<n;i=j+1)   
	{   
		for(j=i;s[j]=='<';++j);     
		reverse(a+i,a+j+1);     
	}   
	for(i=1;i<=n;++i)   
		printf("%d ",a[i]);    
	printf("
");   
	for(i=1;i<=n;++i)   
		a[i]=i;  
	for(i=1;i<n;i=j+1)   
	{
		for(j=i;s[j]=='>';++j);   
		reverse(a+i,a+j+1);   
	}  
	for(i=1;i<=n;++i)   
		printf("%d ",a[i]);    
	printf("
");  
}
int main() 
{ 
	// setIO("input");  
	int i,j,T;  
	scanf("%d",&T);  
	while(T--)   
	    solve();   
	return 0;
}

  

CF1380D

首先,对于任意一个区间来说,都可删掉 $len-1$ 个元素,剩下的元素是最大值.     

那么假设要删掉 $[L,R]$ 区间,可以将 $max(L,R)$ 与 $L-1$ 与 $R+1$ 处元素比较.   

如果最大值小于 $max(a[L-1],a[R+1])$,则一定可以删完,否则必须使用一次连续删的来去掉中间的最大值.    

判断是否有解后每个区间贪心即可.   

#include <cstdio> 
#include <algorithm>  
#include <cstring>     
#define N 300008   
#define ll long long 
#define lson now<<1   
#define rson now<<1|1   
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std;  
int n,m,k;  
ll x,y,ans;  
int a[N],b[N],pos[N],ma[N<<2];  
void build(int l,int r,int now) {  
    if(l==r) {  
        ma[now]=a[l];   
        return; 
    }  
    int mid=(l+r)>>1;  
    build(l,mid,lson),build(mid+1,r,rson); 
    ma[now]=max(ma[lson],ma[rson]); 
}  
int query(int l,int r,int now,int L,int R) {  
    if(l>=L&&r<=R) {    
        return ma[now]; 
    }  
    int mid=(l+r)>>1,re=0;  
    if(L<=mid)  re=max(re,query(l,mid,lson,L,R));  
    if(R>mid)   re=max(re,query(mid+1,r,rson,L,R));  
    return re;  
}
void sol(int L,int R) {  
    if(L==R) return; 
    if(R==L+1) return;   
    int mp=query(1,n,1,L+1,R-1);           
    if(mp<max(a[L],a[R])) {      
        if(x>=(ll)y*k)  {       
            ans+=(ll)(R-L-1)*y;    
        }   
        else {    
            ans+=(ll)x*((R-L-1)/k);    
            int p=(R-L-1)%k;   
            ans+=(ll)y*p;   
        }
    }   
    else {     
        int len=R-L-1;  
        if(len<k) { 
            printf("-1
");           
            exit(0); 
        }   
        len-=k;  
        ans+=x;   
        if(x>=(ll)y*k) { 
            ans+=(ll)len*y;  
        }  
        else { 
            ans+=(ll)x*(len/k);   
            int p=len%k;  
            ans+=(ll)y*p;   
        }
    }
}
int main() {  
    // setIO("input");             
    scanf("%d%d%lld%d%lld",&n,&m,&x,&k,&y);        
    for(int i=1;i<=n;++i) scanf("%d",&a[i]),pos[a[i]]=i;  
    for(int i=1;i<=m;++i) scanf("%d",&b[i]);   
    build(1,n,1);               
    for(int i=2;i<=m;++i) {       
        if(pos[b[i]]<pos[b[i-1]]) { 
            printf("-1
");   
            return 0;   
        }
    }
    sol(0,pos[b[1]]);   
    // printf("%lld
",ans);  
    sol(pos[b[m]],n+1);  
    for(int i=2;i<=m;++i) {    
        sol(pos[b[i-1]],pos[b[i]]);   
    }
    printf("%lld
",ans);                 
    return 0;  
}

  

CF1370D

可以二分答案.    

那么就将问题转化成:数列上一些点是黑点,求最长子序列使得没有黑点是挨着的.    

这个可以直接贪心求(看到白点就选,黑点的话判断一下上一个取的是否是白点).   

然后如果第一个点是白点的话就分 2 中情况讨论一下(这个点算黑点还是算白点)  

#include <cstdio> 
#include <algorithm> 
#define ll long long 
#define N 200009 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
int a[N],A[N],n,K;  
int check(int val) 
{   
    int mx=1,cur=(a[1]<=val);                  
    for(int i=2;i<=n;++i) 
    {
        if(cur==0) 
        {
            if(a[i]<=val) ++mx,cur=1;    
        }  
        else { ++mx,cur=0;}
    }     
    int ans=mx;  
    mx=1,cur=0;     
    for(int i=2;i<=n;++i) 
    {
        if(cur==0) 
        {
            if(a[i]<=val) ++mx,cur=1;    
        }  
        else { ++mx,cur=0;}
    }     
    ans=max(ans,mx);  
    return ans>=K;   
}
int main() 
{
    // setIO("input");
    scanf("%d%d",&n,&K);       
    for(int i=1;i<=n;++i) scanf("%d",&a[i]),A[i]=a[i];   
    sort(A+1,A+1+n);   
    int l=1,r=n,mid,ans=0;  
    while(l<=r) 
    {
        mid=(l+r)>>1;   
        if(check(A[mid])) ans=A[mid],r=mid-1;  
        else l=mid+1;  
    }
    printf("%d
",ans);  
    return 0; 
}

  

CF1363E

显然,如果初始状态 1 的个数和目标状态 1 的个数不同就不合法,否则合法.      

然后考虑节点 $x,y$ 其中 $x$ 是 $y$ 的祖先,那么如果 $v[x] leqslant v[y]$ 的话在 $x$ 处匹配一定优于 $y$ 处匹配.  

换句话说,我们会在 $x$ 处匹配子树中的节点,当且仅当 $v[x]$ 小于祖先的最小值.      

处理好哪些节点可以匹配后贪心匹配就行.      

#include <cstdio>   
#include <cstring>
#include <algorithm> 
#define N 200008  
#define ll long long 
#define inf 1000000009  
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;    
int edges,n;    
ll val[N],mi[N],ans;    
int hd[N],to[N<<1],nex[N<<1];   
int c0[N],c1[N];   
void add(int u,int v) 
{
    nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;  
} 
void dfs(int x,int ff) 
{ 
    int flag=0; 
    if(val[x]<mi[ff]) flag=1;     
    mi[x]=min(val[x],mi[ff]);   
    for(int i=hd[x];i;i=nex[i]) {
        int y=to[i];  
        if(y==ff) continue;   
        dfs(y,x);     
        c0[x]+=c0[y];  
        c1[x]+=c1[y];   
    }
    if(flag) 
    {
        ans+=(ll)val[x]*min(c0[x],c1[x]);   
        int p=min(c0[x],c1[x]);   
        c0[x]-=p;  
        c1[x]-=p;    
    }
}
int main() {
    // setIO("input");    
    scanf("%d",&n);    
    int s1=0,s2=0,x,y,z;  
    for(int i=1;i<=n;++i) {
        scanf("%lld%d%d",&val[i],&x,&y);    
        s1+=x,s2+=y;    
        if(x!=y) {
            if(x==0) ++c1[i];   
            else ++c0[i];  
        }
    }
    for(int i=1;i<n;++i) {
        scanf("%d%d",&x,&y);   
        add(x,y),add(y,x);      
    }
    if(s1!=s2) 
    {
        printf("-1
");  
        return 0;  
    } 
    mi[0]=inf;  
    dfs(1,0);  
    printf("%lld
",1ll*2*ans);   
    return 0;    
}

  

CF1359D  

求:$sum(l,r)-max(l,r)$ 的最大值.       

由于元素可能有负数,所以不能贪心.  

但是我们发现所有元素只有可能有 $60$ 种取值,所以直接枚举最大值,然后维护前缀最小值暴力判断就行.  

#include <bits/stdc++.h> 
#define N 100009   
#define inf 1000000000
#define setIO(s) freopen(s".in","r",stdin)   
using namespace std; 
int a[N],n,ans; 
void sol(int val) 
{    
    int mi=0;     
    int sum=0;  
    for(int i=1;i<=n;++i) 
    {
        if(a[i]>val) 
        {
            mi=0;   
            sum=0;          
        }   
        else 
        {
            sum+=a[i];   
            ans=max(ans,sum-mi-val);     
            mi=min(mi,sum);  
        }
    }  
}
int main() 
{
    // setIO("input");  
    scanf("%d",&n);  
    for(int i=1;i<=n;++i) scanf("%d",&a[i]); 
    sol(10);   
    for(int i=-30;i<=30;++i) sol(i);  
    printf("%d
",ans);    
    return 0; 
}

  

CF1349C 

如果一个位置在初始状态下就有相邻颜色相同的色块的话该位置在一开始就会周期性变化.     

否则,一个点变化的时间就是周围与其颜色不同的点变成与该点颜色相同的时间.  

这个东西可以用最短路求.     

求完 $f[x]$ 表示 $x$ 点开始变化的时间后和 p 分类讨论一下就行了.  

#include <bits/stdc++.h>      
#define N 1009
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std;   
int dx[]={-1,0,1,0};           
int dy[]={0,-1,0,1};         
char g[N][N];   
int a[N][N],mk[N][N],vis[N][N],f[N][N],n,m,Q;     
struct node {  
    int y,x;  
    node(int y=0,int x=0):y(y),x(x){}                    
};  
queue<node>q;  
int main() {
    // setIO("input");     
    scanf("%d%d%d",&n,&m,&Q);  
    for(int i=1;i<=n;++i) {     
        scanf("%s",g[i]+1);   
        for(int j=1;j<=m;++j)   
            a[i][j]=g[i][j]-'0';   
    }                        
    for(int i=1;i<=n;++i) { 
        for(int j=1;j<=m;++j) {     
            for(int t=0;t<4;++t) { 
                int y=i+dy[t];  
                int x=j+dx[t];  
                if(y>=1&&y<=n&&x>=1&&x<=m) {    
                    if(a[y][x]==a[i][j]) mk[i][j]=1;   
                }
            }
        }
    }          
    for(int i=1;i<=n;++i) {     
        for(int j=1;j<=m;++j) {    
            if(mk[i][j]) { 
                vis[i][j]=1; 
                f[i][j]=0;   
                q.push(node(i,j));   
            }
        }
    }     
    while(!q.empty()) { 
        node e=q.front(); q.pop();  
        for(int t=0;t<4;++t) { 
            int y=e.y+dy[t];  
            int x=e.x+dx[t];    
            if(y>=1&&y<=n&&x>=1&&x<=m&&!vis[y][x]&&!mk[y][x]) {    
                vis[y][x]=1;   
                f[y][x]=f[e.y][e.x]+1;    
                q.push(node(y,x));    
            }
        }
    }     
    for(int i=1;i<=Q;++i) { 
        int y,x;     
        ll p; 
        scanf("%d%d%lld",&y,&x,&p);   
        if(!vis[y][x]) {    
            printf("%d
",a[y][x]);  
        }  
        else {      
            if(p<=f[y][x]) printf("%d
",a[y][x]);   
            else {      
                ll t=(p-f[y][x])&1;  
                if(t) printf("%d
",!a[y][x]);  
                else printf("%d
",a[y][x]);  
            }
        }
    }
    return 0;  
}

  

CF1344B

题中要求每一行与每一列至少要有一个 S.              

那么假如说一行中有 2 个黑色块且黑色快没有相邻,那么当 N 走到其中一个黑色块的时候必然会走向该行的 S 而经过白色色块.   

所以如果说一行/一列中有不相邻黑色块的话一定无解.          

如果有一行没有黑色块,那么 S 就要放到与这一行交叉的列且同样没有黑色块的格子上.            

所有如果说只有行/列没有黑色块的话问题同样无解.   

判断好解是否存在后,我们发现可以把每个黑色块上都放上一个 S,那么 N 的个数就是黑色连通块个数了.  

#include <cstdio> 
#include <cstring>
#include <algorithm>         
#define N 1008  
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std; 
int dx[]={-1,0,1,0};   
int dy[]={0,-1,0,1};     
char g[N][N];
int n,m,id[N][N];    
int p[N*N];    
int find(int x) {  
    return p[x]==x?x:p[x]=find(p[x]);  
}
int main() {  
    // setIO("input");     
    scanf("%d%d",&n,&m);    
    for(int i=1;i<=n;++i) {     
        scanf("%s",g[i]+1);  
    }
    int r=0,c=0;  
    for(int i=1;i<=n;++i) {           
        int st=0;  
        for(int j=1;j<=m;++j) {     
            if(g[i][j]=='#'&&(j==1||g[i][j-1]!='#')) ++st;   
        }
        if(st>1) {
            printf("-1
");  
            return 0;              
        }   
        if(st==0) ++r;  
    }     
    for(int i=1;i<=m;++i) {   
        int st=0;  
        for(int j=1;j<=n;++j) {     
            if(g[j][i]=='#'&&(j==1||g[j-1][i]!='#')) ++st;  
        } 
        if(st>1) { 
            printf("-1
");     
            return 0;  
        }  
        if(st==0) ++c;  
    }   
    if((r>0)!=(c>0)) {  
        printf("-1
");      
    }
    else {      
        int cnt=0; 
        for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { 
            id[i][j]=++cnt;  
            p[cnt]=cnt;  
        }    
        for(int i=1;i<=n;++i) { 
            for(int j=1;j<=m;++j) {     
                if(g[i][j]=='.') continue;   
                for(int t=0;t<4;++t) {  
                    int y=i+dy[t];  
                    int x=j+dx[t];  
                    if(y>=1&&y<=n&&x>=1&&x<=m&&g[y][x]=='#')  {         
                        int a=id[i][j],b=id[y][x];  
                        a=find(a),b=find(b); 
                        if(a!=b) p[a]=b;   
                    }
                }
            }
        }
        int ans=0; 
        for(int i=1;i<=n;++i) { 
            for(int j=1;j<=m;++j) { 
                if(g[i][j]=='#'&&find(id[i][j])==id[i][j]) ++ans;  
            }
        }  
        printf("%d
",ans); 
    }  
    return 0;
}

  

CF1316D 

可以从 $(a_{x},a_{y})=(x,y)$ 的地方 BFS,然后构造出所有 $a_{x}$ 不等于 $-1$ 的解.    

这里如果说一个点没有被 BFS 到的话就无解.   

对于无限走的格子,我们可以两两匹配,或者将其连向相邻的已经匹配的格子.  

那么这里无解的条件就是一个格子相邻的都是 $a_{x}$ 不等于 1 的格子.         

#include <bits/stdc++.h>      
#define N 1009  
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std; 
int dx[]={-1,0,1,0};   
int dy[]={0,-1,0,1};       
char ge[]={'R','D','L','U'};          
struct node {  
    int y,x;  
    node(int y=0,int x=0):y(y),x(x){}  
}g[N][N]; 
int vis[N][N]; 
char ans[N][N];     
queue<node>q;  
int main() {  
    // setIO("input");      
    int n;  
    scanf("%d",&n);  
    for(int i=1;i<=n;++i) {  
        for(int j=1;j<=n;++j) {  
            scanf("%d%d",&g[i][j].y,&g[i][j].x);   
            if(g[i][j].y==i&&g[i][j].x==j) { 
                q.push(node(i,j));   
                ans[i][j]='X';     
                vis[i][j]=1;   
            }
        }
    }    
    while(!q.empty()) {     
        node e=q.front(); q.pop();             
        for(int t=0;t<4;++t) { 
            int y=e.y+dy[t];  
            int x=e.x+dx[t];   
            if(y>=1&&y<=n&&x>=1&&x<=n) {  
                if(vis[y][x]||g[y][x].y==-1)  continue;       
                if(g[y][x].y==g[e.y][e.x].y&&g[y][x].x==g[e.y][e.x].x) {    
                    vis[y][x]=1;  
                    ans[y][x]=ge[t];    
                    q.push(node(y,x));   
                }
            }
        }
    }
    for(int i=1;i<=n;++i) { 
        for(int j=1;j<=n;++j) {         
            if(g[i][j].y!=-1&&!vis[i][j]) {  
                // printf("%d %d %d %d
",i,j,g[i][j].y,g[i][j].x); 
                printf("INVALID
");  
                return 0;  
            }
        }
    }       
    for(int i=1;i<=n;++i) {  
        for(int j=1;j<=n;++j) {     
            if(g[i][j].y==-1) {     
                if(vis[i][j]) continue;       
                int c=0; 
                for(int t=0;t<4;++t) {     
                    int y=i+dy[t];  
                    int x=j+dx[t];  
                    if(g[y][x].y==-1) {     
                        ++c;   
                        if(vis[y][x]) {     
                            ans[i][j]=ge[(t+2)%4];     
                            vis[i][j]=1;  
                            break;  
                        }  
                        else {  
                            ans[i][j]=ge[(t+2)%4];           
                            ans[y][x]=ge[t];  
                            vis[i][j]=vis[y][x]=1;   
                            break;  
                        }
                    }
                }
                if(!c) {  
                    printf("INVALID
");  
                    return 0;  
                }
            }
        }
    }
    printf("VALID
");   
    for(int i=1;i<=n;++i) { 
        for(int j=1;j<=n;++j) printf("%c",ans[i][j]);  
        printf("
");  
    }
    return 0;   
}

  

CF1311D

答案一定不超过 $3 imes 10^4$,那么每一个位置的数字也不可能大于 $2 imes 10^4$.  

所以直接暴力枚举 $a$ 的取值,然后调和级数枚举 $b$,$c$ 的取值 $O(1)$ 分类讨论就好了.  

#include <set>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define M 20000
#define N 100009
#define setIO(s) freopen(s".in","r",stdin)
using namespace std; 
struct data { 
    int a,b,c,val; 
    data(int x=0,int y=0,int z=0,int p=(M<<2)) {  
        a=x; 
        b=y; 
        c=z; 
        val=p; 
    }       
    data operator+(const data b) const { 
        return b.val<val?b:*this;      
    }
}k;  
void solve() {  
    int a,b,c;  
    data ans=data();  
    scanf("%d%d%d",&a,&b,&c);   
    for(int i=1;i<=M;++i) {       
        int tmp=abs(i-a);  
        for(int j=i;j<=M;j+=i) {    
            int cnt=0;  
            cnt=tmp+abs(j-b);  
            if(c<=j) { 
                cnt+=abs(c-j);   
                ans=ans+data(i,j,j,cnt); 
            } 
            else {  
                int pp=cnt;  
                cnt+=c-(j*(c/j));          
                pp+=(j*(c/j+1))-c;   
                ans=ans+data(i,j,j*(c/j),cnt);    
                ans=ans+data(i,j,(j*(c/j+1)),pp); 
            }   
        }
    }    
    printf("%d
",ans.val); 
    printf("%d %d %d
",ans.a,ans.b,ans.c);  
}
int main() {
    // setIO("input");
    int T;  
    scanf("%d",&T);  
    while(T--) {  
        solve(); 
    }
    return 0;
}

  

原文地址:https://www.cnblogs.com/guangheli/p/12423513.html