好 不 爽 → u n r a t e d → s o l v e j u s t f o r f u n ? 好不爽\rightarrow unrated\rightarrow solve~ just~ for~ fun? 好不爽→unrated→solve just for fun?
A A A
题意:拼图,每块拼图部件有一个凹三个凸,只有凹凸可以贴合.问能否拼成 n ∗ m n*m n∗m的图.
每个点有1个出度,3个入度.(边界的点看作对外有出度)
那么 n = 1 ∪ m = 1 n=1\cup m=1 n=1∪m=1 一定可以.
而 n = m = 2 n=m=2 n=m=2,由样例得可行.
对于其他情况一定包含一个 2 ∗ 3 或 3 ∗ 2 2*3或3*2 2∗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;
}
B B B
容易看出一个DP转移函数:
f [ i ] f[i] f[i]表示高度为 i i i要多少张牌.
{ f [ 0 ] = 0 , f [ 1 ] = 1 f [ i ] = 2 f [ i − 1 ] − f [ i − 2 ] + 3 ( i > 1 ) \begin{cases}f[0]=0,f[1]=1\\f[i]=2f[i-1]-f[i-2]+3(i>1)\end{cases} {f[0]=0,f[1]=1f[i]=2f[i−1]−f[i−2]+3(i>1)
打表找到上界 i i i,然后每次用二分来查数即可.
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.5 ∗ n 2 + 0.5 n f[n]=1.5*n^2+0.5n f[n]=1.5∗n2+0.5n
解方程即可.
C C C
一开始题目看不懂,QAQ…
题意:给你 n n n个整数 a 0 , a 1 . . . . a n − 1 a_0,a_1....a_{n-1} a0,a1....an−1,判断是否不存在 i ≠ j 且 i + a [ i m o d n ] = j + a [ j m o d n ] i\ne j且i+a[i\mod n]=j+a[j\mod n] i=j且i+a[imodn]=j+a[jmodn] .
首先对于一个同余系的数一定不会冲突.
然后我们枚举 i , j ( 0 ≤ i < j < n ) i,j(0\le i<j<n) i,j(0≤i<j<n),判断是否
i + a [ i ] ≡ j + a [ j ] ( m o d n ) i+a[i]\equiv j+a[j](\mod n) i+a[i]≡j+a[j](modn)
开个bool数组判断 ( i + a [ i ] ) m o d n (i+a[i])\mod n (i+a[i])modn是否互不相同即可.
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;
}
D D D
题意:
给定 n ∗ m n*m n∗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;
}
实际可用并查集来合并行列(因为每行/列至多有一段).
E E E
题目好绕,对我很不友好.
简 明 题 意 : 有 n 个 变 量 x 1 , x 2 . . . x n , m 条 相 互 关 系 ( x a j < x b j , j ∈ [ 1 , m ] ) , 设 简明题意:有n个变量x_1,x_2...x_n,m条相互关系(x_{a_j}<x_{b_j},j\in[1,m]),设 简明题意:有n个变量x1,x2...xn,m条相互关系(xaj<xbj,j∈[1,m]),设 f ( x ) = ∩ j = 1 m x a j < x b j ( ∩ 指 的 是 a n d 运 算 ) ) , 最 后 的 令 Q 1 x 1 , Q 2 x 2 . . Q n x n f ( x ) = t r u e , Q i = ∀ / ∃ 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 f(x)=∩j=1mxaj<xbj(∩指的是and运算)),最后的令Q1x1,Q2x2..Qnxnf(x)=true,Qi=∀/∃
最后,求满足条件的最多的 ∀ \forall ∀数量
我们把相互关系看作 ( a j , b j ) (a_j,b_j) (aj,bj),即一条有向边.
则显然,如果成环则无论如何都不合法,输出 − 1. ( x < x ) -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.
F F F
定义二元函数 f ( x , y ) = x ( y − x 2 ) f(x,y)=x(y-x^2) f(x,y)=x(y−x2)
我们考虑增量法:把x-1弄到x-> Δ = f ( x , a [ i ] ) − f ( x − 1 , a [ i ] ) = a − 1 − 3 x ( x − 1 ) \Delta =f(x,a[i])-f(x-1,a[i])=a-1-3x(x-1) Δ=f(x,a[i])−f(x−1,a[i])=a−1−3x(x−1).
显然这是一个当 x ≥ 1 x\ge 1 x≥1时递减的函数.
一个贪心的做法是每次取最大的变化量,复杂度 O ( k n ) O(kn) O(kn),配合堆优化可 O ( k log n ) O(k\log n) O(klogn)
但是k太大了,不能直接处理.
我们要利用到增量的递减性质.
由贪心思路得:每次选择的变化量不严格递减.
所以,如果我们确定最后一个变化量,即可求出最多变化多少次.这个考虑二分处理: l = min i = 1 n f ( a [ i ] , a [ i ] ) , r = max i = 1 n f ( 1 , a [ i ] ) l=\min_{i=1}^n f(a[i],a[i]),r=\max_{i=1}^n f(1,a[i]) l=mini=1nf(a[i],a[i]),r=maxi=1nf(1,a[i])
最后还剩下至多 n n n个数可选择,对还能选择的量进行排序贪心选大的即可.
ps:对于每个 a i a_i ai,求最多增加几次的时候可二分可用解二次方程的方法
复杂度: O ( n log ( r − l ) log m a x a i ) O(n\log (r-l)\log maxai) O(nlog(r−l)logmaxai)
#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;
}