已补 2 / 7 2/7 2/7
CodeForces - 510E
题意
给定多个数,形成多个环,保证环上相邻和是素数。
题解
刘大爷提示网络流之后就开始往那方面想。
把题目转换成,找
n
n
n条边,并且保证每个点只和两条边相连。
但是怎么建图都只能限制度数或者边数。
题解是采用了素数=奇数+偶数。也就是每条选择的边一定是奇数和偶数。
设置一个二分图,左边奇数,右边偶数,一旦选择一条边,就会用掉一个点的度数。选择
n
n
n条边就会用掉
2
n
2n
2n的度数。这样可以保证点的度数不会出现只用掉了边上的一个点的度数,而且两个都计算了。
非常巧妙。
之前设置的边数是对的,但是可能会出现某个点度数过大(以边连点)。
反过来以点连边,需要有些边必须被选择两次,而实际可能只跑一个流量。
两者都是不可取的。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100050;
#define inf 0x3f3f3f3f
typedef long long ll;
struct Edge{
int from,to;
ll cap,flow;
};
vector<int>T[maxn];
vector<int>ans[maxn];
bool vis[maxn];
struct Dinic{
int n,tmp,s,t,ttt;
vector<Edge>edges;
vector<int>G[maxn];//邻接表用,存储的是边在edges中的序号
bool vis[maxn];//BFS使用
int d[maxn];//从起点到i的距离
int cur[maxn];//当前弧下标
void init(int n,int s,int t,int tttt){
this->n=n,this->s=s,this->t=t,this->ttt=tttt;
edges.clear();
for(int i=1;i<=n;i++)G[i].clear();
}
void AddEdge(int from,int to,ll cap){
edges.push_back((Edge){from,to,cap,0});
edges.push_back((Edge){to,from,0,0});
tmp=edges.size();
G[from].push_back(tmp-2);
G[to].push_back(tmp-1);
}
bool BFS(){
memset(vis,0,sizeof(vis));
queue<int>q;
q.push(s);
d[s]=0,vis[s]=1;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<G[x].size();i++){
Edge& e = edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow){
vis[e.to]=1;
d[e.to]=d[x]+1;
q.push(e.to);
}//只考虑残量网络中的弧
}
}
return vis[t];//用于判断是否能走到底。
}
ll DFS(int x,ll a){//多路增广
if(x==t||a==0)return a;//a表示的是当前最小,也就是接下来能用的不能超过a
ll flow=0,f;
for(int& i=cur[x];i<G[x].size();i++){//能保证一个dfs中不重复走同样的边(对于同一个节点),因为走过的边一定是满载的了。
Edge& e = edges[G[x][i]];
if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){
e.flow+=f;
edges[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0)break;//f表示从这个e.to的点开始使用的最大流。
}
}
return flow;
}
ll Maxflow(){
ll flow=0;
while(BFS()){
memset(cur,0,sizeof(cur));
flow+=DFS(s,inf);
}
return flow;
}
bool del(){
if(Maxflow()!=ttt)return false;
for(int i=1;i<=ttt;i++){
for(int j=0;j<G[i].size();j++){
int f=edges[G[i][j]].flow;
int v=edges[G[i][j]].to-ttt;
if(v>=1&&v<=ttt);
else continue;
if(f){
T[i].push_back(v);
T[v].push_back(i);
}
}
}
return true;
}
}dc;
int A[maxn];
void dfs(int u,int now){
ans[now].push_back(u);
vis[u]=true;
for(auto v:T[u]){
if(vis[v])continue;
dfs(v,now);
}
}
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++)scanf("%d",&A[i]);
dc.init(2*n+2,2*n+1,2*n+2,n);
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
int now=A[i]+A[j];
bool ok=true;
for(int k=2;k<=sqrt(now);k++){
if(now%k==0)ok=false;
}
if(ok){
int l,r;
if(A[i]%2)l=i,r=n+j;
else l=j,r=n+i;
//cout<<l<<" "<<r-n<<endl;
dc.AddEdge(l,r,1);
}
}
}
for(int i=1;i<=n;i++){
if(A[i]%2){
dc.AddEdge(2*n+1,i,2);
}
}
for(int i=1;i<=n;i++){
if(A[i]%2==0){
dc.AddEdge(n+i,2*n+2,2);
}
}
int cnt=0;
if(dc.del()){
for(int i=1;i<=n;i++){
if(vis[i])continue;
dfs(i,++cnt);
}
cout<<cnt<<endl;
for(int i=1;i<=cnt;i++){
cout<<ans[i].size()<<" ";
for(auto x:ans[i])printf("%d ",x);
puts("");
}
}
else puts("Impossible");
}
CodeForces - 518E
题意
对于
a
a
a数组,有生成新序列
(
a
1
+
a
2...
+
a
k
,
a
2
+
a
3
+
.
.
.
+
a
k
+
1
,
.
.
.
,
a
n
−
k
+
1
+
a
n
−
k
+
2
+
.
.
.
+
a
n
)
(a1 + a2 ... + ak, a2 + a3 + ... + ak + 1, ..., an - k + 1 + an - k + 2 + ... + an)
(a1 + a2... + ak, a2 + a3 + ... + ak + 1, ..., an − k + 1 + an − k + 2 + ... + an)。
但原数组存在未知数
?
?
?并且有多个,要求新序列是严格递增的,构造一个合法原序列(赋值
?
?
?)
要求
?
?
?的绝对值和最小。
题解
简单推一下可以推出
a
i
<
a
k
+
i
a_i<a_{k+i}
ai<ak+i
也就是只需要维护
k
k
k个递增序列。
对于每个递增序列
l
,
?
,
.
.
.
,
?
,
r
l,?,...,?,r
l,?,...,?,r的情况。
如果区间全正全负,显然就是往
0
0
0靠。
如果区间过
0
0
0且大小合适,就是以中间往左右扩展。
具体做法就是:从 l l l开始往右,从 r r r开始往左,判断是否能扩展,从而在中间往右扩展,由于奇数的情况就采用两种办法判合法即可(其实可能不要)。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5+5000;
typedef long long ll;
int n,k,now,pre;
int A[maxn],vis[maxn];
vector<int>G;
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
string str;cin>>str;
if(str=="?")vis[i]=1;
else{
if(str[0]=='-'){
str=str.substr(1);
A[i]=-atoi(str.c_str());
}
else A[i]=atoi(str.c_str());
}
}
for(int i=1;i<=k;i++)A[n+i]=0x3f3f3f3f;
bool ok=true;
for(int i=1;i<=k;i++){
now=0,G.clear();
pre=-0x3f3f3f3f;
for(int j=i;j<=n+k;j+=k){
if(vis[j])now++,G.push_back(j);
else{
//cout<<j<<" "<<A[j]<<" "<<now<<" "<<ok<<endl;
if(now==0){
if(A[j]>pre){
pre=A[j];
continue;
}
else{
ok=false;
break;
}
}
int r=max(A[j],pre),l=min(A[j],pre);
int RR,LL;
ll ans=0,tmp;
if(r-l-1>=now){
RR=r,LL=l;
for(int x=now-1;x>=0;x--){
A[G[x]]=--RR;
ans+=abs(A[G[x]]);
}
RR=r,LL=l,tmp=0;
for(int x=0;x<now;x++)tmp+=abs(++LL);
if(tmp<ans){
ans=tmp;
LL=l;
for(int x=0;x<now;x++)A[G[x]]=++LL;
}
int st;
st=-now/2;
if(l<-now/2&&-now/2+now-1<r){
tmp=0;
for(int x=0;x<now;x++)tmp+=abs(st++);
if(tmp<ans){
ans=tmp;
st=-now/2;
for(int x=0;x<now;x++)A[G[x]]=st++;
}
}
st=now/2;
if(l<now/2-(now-1)&&now/2<r){
tmp=0;
for(int x=now-1;x>=0;x--)tmp+=abs(st--);
if(tmp<ans){
ans=tmp;
st=now/2;
for(int x=now-1;x<now;x++)A[G[x]]=st--;
}
}
}
else{
ok=false;
break;
}
pre=A[j];
now=0;
G.clear();
}
}
if(ok==false)break;
}
for(int i=1;i<=k;i++){
pre=-0x3f3f3f3f;
for(int j=i;j<=n;j+=k){
if(A[j]<=pre)ok=false;
pre=A[j];
}
}
if(ok){
for(int i=1;i<=n;i++)printf("%d ",A[i]);
}
else puts("Incorrect sequence");
}