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的悲剧....