题意:拼图,每块拼图部件有一个凹三个凸,只有凹凸可以贴合.问能否拼成 的图.
每个点有1个出度,3个入度.(边界的点看作对外有出度)
那么 一定可以.
而 ,由样例得可行.
对于其他情况一定包含一个 的矩阵.
而这个矩阵内有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;
}
容易看出一个DP转移函数:
表示高度为 要多少张牌.
打表找到上界 ,然后每次用二分来查数即可.
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;
}
更快的做法:
可以发现
解方程即可.
一开始题目看不懂,QAQ…
题意:给你 个整数 ,判断是否不存在 .
首先对于一个同余系的数一定不会冲突.
然后我们枚举 ,判断是否
开个bool数组判断 是否互不相同即可.
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;
}
题意:
给定 的颜色(黑白)矩阵.开始随意放置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;
}
实际可用并查集来合并行列(因为每行/列至多有一段).
题目好绕,对我很不友好.
最后,求满足条件的最多的 数量
我们把相互关系看作 ,即一条有向边.
则显然,如果成环则无论如何都不合法,输出
然后,任意两个有大小关系的数至多有一个 .
所以我们用拓扑判环+正反向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.
定义二元函数
我们考虑增量法:把x-1弄到x-> .
显然这是一个当 时递减的函数.
一个贪心的做法是每次取最大的变化量,复杂度 ,配合堆优化可
但是k太大了,不能直接处理.
我们要利用到增量的递减性质.
由贪心思路得:每次选择的变化量不严格递减.
所以,如果我们确定最后一个变化量,即可求出最多变化多少次.这个考虑二分处理:
最后还剩下至多 个数可选择,对还能选择的量进行排序贪心选大的即可.
ps:对于每个 ,求最多增加几次的时候可二分可用解二次方程的方法
复杂度:
#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;
}