Codeforces Round #533 (Div. 2)
Codeforces Round #533 (Div. 2)题解
A
题意:
给你一个序列,你可以修改序列中的每个数使其变成一个另一个正整数,如a-->b代价为abs(a-b)
求一个t是的修改后的(abs)(a[i]-t)<=1,求最小代价
题解:
因为a很小枚举即可
#include<bits/stdc++.h>
using namespace std;
int a[1005];
int main()
{
int n;
scanf("%d",&n);
for (int i=1; i<=n; i++) scanf("%d",&a[i]);
int ans1=2000000000,ans2=a[n];
for (int t=1; t<=100; t++)
{
int sum=0;
for (int j=1; j<=n; j++)
{
int zz=2000000000;
for (int tt=t-1; tt<=t+1; tt++) if (tt>0)
{
zz=min(zz,abs(tt-a[j]));
}
sum+=zz;
}
if (sum<ans1)
{
ans1=sum; ans2=t;
}
}
printf("%d %d\n",ans2,ans1);
}
B
题意:
给一个字符串和k,连续k个相同的字符,可使等级x加1,
例:8 2 aaacaabb 则有aa aa 即x=2。 求最大的x
题解:
O(N)扫一遍,对于每一段统计一下答案
#include<bits/stdc++.h>
using namespace std;
int n,k;
char s[200005];
int sum[10000];
int main()
{
scanf("%d%d",&n,&k);
scanf("%s",s+1);
int pre=0,ans=0,len=0;s[n+1]='A'-1;
for (int i=1; i<=n+1; i++)
{
int x=s[i]-'A'+1;
if (x!=pre)
{
sum[pre]+=len/k;
//cout<<x<<" "<<sum[x]<<" "<<len/k<<endl;
ans=max(ans,sum[pre]);
len=1;
}
else len++;
pre=x;
}
printf("%d\n",ans);
return 0;
}
C
题意:
一个n长的数组每个位置可以填区间l-r的值。
有多少种填法,使得数组每个位置相加的和是3的倍数
题解:
统计一下l到r mod 3为0、1、2的个数 之后DP一下就可以了
#include<bits/stdc++.h>
#define N 200005
#define mod 1000000007
using namespace std;
long long f[N][4];
int n,l,r;
long long a[4];
int main()
{
scanf("%d%d%d",&n,&l,&r);
a[0]=a[1]=a[2]=(r-l+1)/3;
int xx=(r-l+1-a[0]*3);
if (xx==1)
{
if (r%3==0) a[0]++;
if (r%3==2) a[2]++;
if (r%3==1) a[1]++;
}
else if (xx==2)
{
a[0]++; a[1]++; a[2]++;
if (r%3==0) a[1]--;
if (r%3==2) a[0]--;
if (r%3==1) a[2]--;
}
// cout<<" "<<a[0]<<" "<<a[1]<<" "<<a[2]<<endl;
f[1][1]=a[1];
f[1][2]=a[2];
f[1][0]=a[0];
for (int i=2; i<=n; i++)
{
f[i][0]=((f[i-1][0]*a[0]%mod+f[i-1][1]*a[2]%mod)%mod+f[i-1][2]*a[1]%mod)%mod;
f[i][1]=((f[i-1][0]*a[1]%mod+f[i-1][1]*a[0]%mod)%mod+f[i-1][2]*a[2]%mod)%mod;
f[i][2]=((f[i-1][0]*a[2]%mod+f[i-1][1]*a[1]%mod)%mod+f[i-1][2]*a[0]%mod)%mod;
}
printf("%lld",f[n][0]);
return 0;
}
D
题意:
给n*m的地图,最多9个人,同时有每个人的扩张次数(我开始以为是直线扩张最大长度。。实际是能连续扩张次数。)
地图上有‘#’,‘.',和数字,数字对应每个人的据点,
从1-n轮流扩张。
地图被扩张完后,输入每个人的据点数目。
题解:
开p个队列,模拟一下就可以了
#include<bits/stdc++.h>
using namespace std;
int n,m,p;
int dx[5]={0,0,-1,1},dy[5]={1,-1,0,0};
struct Node{
int x,y;
};
queue<Node> q[20];
int ans[20],speed[20];
char s[1005][1005];
void run(int i)
{
for (int tt=q[i].size()-1; tt>=0; tt--)
{
Node nownode=q[i].front(); ans[i]++;
q[i].pop();
for (int k=0; k<4; k++)
{
int xx=nownode.x+dx[k],yy=nownode.y+dy[k];
if (xx<1 || xx>n || yy<1 || yy>m) continue;
if (s[xx][yy]=='.')
{
s[xx][yy]='0'+i;
Node newnode={xx,yy};
q[i].push(newnode);
}
}
}
}
void init()
{
scanf("%d%d%d",&n,&m,&p);
for (int i=1; i<=p; i++) scanf("%d",&speed[i]);
for (int i=1; i<=n; i++)
{
scanf("%s",s[i]+1);
for (int j=1; j<=m; j++)
{
if (s[i][j]=='.' || s[i][j]=='#') continue;
int X=s[i][j]-'1'+1;
Node newnode={i,j};
q[X].push(newnode);
}
}
}
int main()
{
init();
while (true)
{
for (int i=1; i<=p; i++)
{
for (int j=1; (!q[i].empty() && j<=speed[i]); j++) run(i);
}
int sum=0;
for (int i=1; i<=p; i++) sum+=q[i].size();
//cout<<sum<<endl;
if (sum==0) break;
}
for (int i=1; i<=p; i++) printf("%d ",ans[i]);
}
E
题意:
有 n 个事件,op = 1 表示我可以修改昵称,op = 2 表示一个名为 s_i 的朋友查询我当前的名字。一个朋友是高兴的当且仅当他每次查询我的名字都为 s_i,保证每个朋友至少查询一次我的名字,问最多可以有多少个朋友高兴。
题解:
之前以为只要高兴一次就可以了 后面才发现每次高兴才算高兴
只高兴一次就算高兴的话 可以用网络流写
1.对于每次修改账号的点 从S连一条容量为1的边
2.对于每次查询,从之前的修改向其连一条容量为1的边
3.对于每个人向T连一条容量为1的边
#include<bits/stdc++.h>
#define N 1005
#define INF 100000000
using namespace std;
int n,m;
int ans;
int tot1,tot2;
map<string,int> mp;
struct Edge{
int from,to,cap,flow,pre;
};
struct Dinic{
int n,m,s,t,edgenumber;
int now[N],dis[N];
bool vis[N];
vector<Edge> edges;
void preclear()
{
edgenumber=0;
edges.clear();
memset(now,-1,sizeof(now));
}
void AddEdge(int from,int to,int cap)
{
// cout<<" "<<from<<" "<<to<<" "<<cap<<endl;
Edge E1={from,to,cap,0,now[from]};
edges.push_back(E1);
now[from]=edgenumber++;
Edge E2={to,from,0,0,now[to]};
edges.push_back(E2);
now[to]=edgenumber++;
}
bool BFS()
{
queue<int> Q;
memset(vis,0,sizeof(vis));
memset(dis,-1,sizeof(dis));
Q.push(s); vis[s]=1;
while (!Q.empty())
{
int x=Q.front(); Q.pop();
//cout<<x<<endl;
for (int p=now[x]; p!=-1; p=edges[p].pre)
{
Edge e=edges[p];
if (!vis[e.to] && e.cap>e.flow)
{
vis[e.to]=1; dis[e.to]=dis[x]+1;
Q.push(e.to);
}
}
}
return vis[t];
}
int dfs(int x,int a)
{
if (x==t || a==0)
{
return a;
}
int flow=0,f;
for (int p=now[x]; p!=-1; p=edges[p].pre)
{
Edge e=edges[p];
if (dis[e.to]==dis[x]+1 && (f=dfs(e.to,min(a,e.cap-e.flow))) >0)
{
edges[p].flow+=f;
edges[p^1].flow-=f;
a-=f;
flow+=f;
if (a==0) break;
}
}
return flow;
}
int maxflow(int s,int t)
{
this -> s=s;
this -> t=t;
int flow=0;
while (BFS())
{
//cout<<"shit"<<endl;
flow+=dfs(s,INF);
}
return flow;
}
}D;
int main()
{
D.preclear();
scanf("%d%d",&n,&m);
int pre=-1,prex=0,s,t; tot1=1;
s=0; t=1;
for (int i=1; i<=n; i++)
{
int type;
scanf("%d",&type);
if (type==1)
{
++tot1;
pre=tot1;
prex=1;
}
else
{
string ss;
if (prex==1)
D.AddEdge(s,pre,1);
cin>>ss;
if (mp.find(ss)==mp.end())
{
mp[ss]=++tot1;
D.AddEdge(mp[ss],t,1);
}
if (pre!=-1) D.AddEdge(pre,mp[ss],1);
prex=2;
}
}
cout<<D.maxflow(s,t)<<endl;
return 0;
}
每次高兴才算高兴的话
其实就是对于一段连续的查询操作统计答案时最多只能统计一次
所以对于一段连续的查询操作中任意两点连边所形成的图的 补集的最大团就是答案
之后利用最大团的随机算法就好
至于随机算法证明我就只能告诉你应该和概率相关,具体不会证明
#include<bits/stdc++.h>
using namespace std;
int n,m,k,a[50],b[50],tot;
bool vis[50][50];
string ss;
map<string,int>mp;
bitset<50>s,ans;
void work1()
{
for (int i=1; i<=a[0]; i++)
{
for (int j=i+1; j<=a[0]; j++)
vis[a[i]][a[j]]=vis[a[j]][a[i]]=false;
}
a[0]=0;
}
void work2()
{
if (!mp.count(ss)) mp[ss]=++tot;
a[++a[0]]=mp[ss];
}
int main()
{
scanf("%d%d",&n,&m);
memset(vis,true,sizeof(vis));
for (int i=1; i<=n; i++)
{
int type;
scanf("%d",&type);
if (type==1) work1();
else cin>>ss,work2();
}
work1();
int T=5e4;
for (int i=1; i<=m; i++) b[i]=i;
while (T--)
{
random_shuffle(b+1,b+m+1);
for (int i=1; i<=m; i++) s[i]=1;
for (int i=1; i<=m; i++)
{
if (s[b[i]]==1)
{
for (int j=i+1; j<=m; j++)
{
if (!vis[b[i]][b[j]]) s[b[j]]=0;
}
}
}
if (s.count()>ans.count()) ans=s;
}
cout<<ans.count()<<endl;
}