冲刺国赛模拟 9
既然 2 号那就说明明天可以摆一天了。开一下挂了好久没学的一些东西,先不做 AGC 了。
喜报:我多项式没出门!
哈密顿路
首先哈密顿路有个经典状压 dp:\(dp_{i,S}\) 为以 \(i\) 结尾能否经过 \(S\) 所有点。然后一个经典优化是把 \(i\) 这一维拿 int 压起来,复杂度 \(O(n2^n)\)。然后找答案就枚举一个端点和在它那一边的集合 \(S\) 就能算了。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <iostream>
using namespace std;
int n,dp[1<<24],a[25];
char s[30];
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%s",s);
for(int j=0;j<n;j++)a[i]|=(s[j]-'0')<<j;
}
dp[1]=1;
for(int s=1;s<(1<<n);s++){
for(int i=0;i<n;i++){
if((s>>i)&1)continue;
if(dp[s]&a[i])dp[s|(1<<i)]|=1<<i;
}
}
const int U=(1<<n)-1;
for(int i=0;i<n;i++){
int ans=0;
for(int s=1;s<(1<<n);s++){
if((dp[s]>>i)&1)ans|=dp[(U^s)|1];
}
for(int j=0;j<n;j++)putchar(((ans>>j)&1)+'0');puts("");
}
return 0;
}
统一省选
赛时玩了一小时克克莉丝小姐 T3 导致没写完,交了一发获得 30 分。
答案是分段函数 \(f(H)=\begin{cases}\text{illegal},&H<x\\a,&x\le H<y\\H+b,&H\ge y\end{cases}\),那随手合并,线段树二分一下。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#define int long long
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,m,H;
pair<int,int>a[1000010];
struct node{
int x,y,a,b;
int calc(int val){
if(val<x)return 0;
else if(val<y)return b;
else return val+a;
}
node operator+(node s){
node ans;
if(max(x,s.x)<inf&&(y<inf||b>=s.x)){
ans.x=x;
if(b<s.x)ans.x=max(ans.x,s.x-a);
}
else ans.x=inf;
if(max(y,s.y)<inf){
ans.y=y;
if(b<s.y)ans.y=max(ans.y,s.y-a);
ans.a=a+s.a;
}
else ans.y=inf,ans.a=0;
ans.b=inf;
if(ans.x<ans.y){
if(b<s.x)ans.b=s.b;
else ans.b=s.calc(b);
}
else ans.b=0;
return ans;
}
}tree[4000010];
void pushup(int rt){
tree[rt]=tree[lson]+tree[rson];
}
void build(int rt,int L,int R){
if(L==R){
if(a[L].first==1)tree[rt]={a[L].second+1,a[L].second+1,-a[L].second,1};
if(a[L].first==2)tree[rt]={a[L].second,inf,0,a[L].second};
if(a[L].first==3)tree[rt]={1,a[L].second,0,a[L].second};
return;
}
int mid=(L+R)>>1;
build(lson,L,mid);build(rson,mid+1,R);
pushup(rt);
}
void update(int rt,int L,int R,int pos){
if(L==R){
if(a[L].first==1)tree[rt]={a[L].second+1,a[L].second+1,-a[L].second,1};
if(a[L].first==2)tree[rt]={a[L].second,inf,0,a[L].second};
if(a[L].first==3)tree[rt]={1,a[L].second,0,a[L].second};
return;
}
int mid=(L+R)>>1;
if(pos<=mid)update(lson,L,mid,pos);
else update(rson,mid+1,R,pos);
pushup(rt);
}
pair<node,int> query(int rt,int L,int R,int l,node pre){
int mid=(L+R)>>1;
if(l>mid)return query(rson,mid+1,R,l,pre);
if(L==R){
node ret=pre+tree[rt];
if(ret.calc(H)<=0)return make_pair(pre,R-1);
else return make_pair(ret,R);
}
if(l<=L){
node ret=pre+tree[lson];
if(ret.calc(H)<=0)return query(lson,L,mid,l,pre);
else return query(rson,mid+1,R,l,ret);
}
pair<node,int> tmp=query(lson,L,mid,l,pre);
if(tmp.second<mid)return tmp;
return query(rson,mid+1,R,l,pre+tmp.first);
}
signed main(){
scanf("%lld%lld%lld",&n,&m,&H);
for(int i=1;i<=n;i++){
int od,x;scanf("%lld%lld",&od,&x);
a[i]=make_pair(od,x);
}
build(1,1,n);
while(m--){
int od;scanf("%lld",&od);
if(od==1){
int x,tp,val;scanf("%lld%lld%lld",&x,&tp,&val);
a[x]=make_pair(tp,val);
update(1,1,n,x);
}
else{
int x;scanf("%lld",&x);
pair<node,int>ans=query(1,1,n,x,{1,1,0,1});
if(ans.second<x)ans.second=-1;
printf("%lld\n",ans.second);
}
}
return 0;
}
并行计算
他说上界是 \(\max\left(\lceil\log_2n\rceil,\left\lceil\dfrac{2n-2}5\right\rceil\right)\),证明好玄学。
分界是 \(x\le 16\),小于的直接爆搜。大于的看到界是 \(0.4n+O(1)\),那么有 \(6\) 步跳过 \(15\) 个的方法,用这个跳即可,再把边界用前边的处理一下。
几个界是 \(2\) 步 \(4\) 个、\(3\) 步 \(8\) 个、\(4\) 步 \(11\) 个、\(6\) 步 \(16\) 个。
直接硬上是 98 分,要枚举有几个 \(16\),剩下的用 \(11\)。然而观察挂的数据剩下的全是 \(6\),因此退一步就行。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
int n,x;
struct node{
pair<int,int>a[4];
};
vector<node>ans;
void add(int a,int b,int c,int d,int e,int f,int g,int h){
node ret;
ret.a[0]=make_pair(x+a,x+b);
ret.a[1]=make_pair(x+c,x+d);
ret.a[2]=make_pair(x+e,x+f);
ret.a[3]=make_pair(x+g,x+h);
ans.push_back(ret);
}
void solve16(){
add(1,2,4,5,7,8,10,11);
add(2,3,5,6,8,9,11,12);
add(3,4,3,5,3,6,13,14);
add(6,7,6,8,6,9,14,15);
add(9,10,9,11,9,12,15,16);
add(12,13,12,14,12,15,12,16);
}
void solve11(){
add(1,2,3,4,5,6,8,9);
add(2,3,2,4,6,7,9,10);
add(4,5,4,6,4,7,10,11);
add(7,8,7,9,7,10,7,11);
}
void solve8(){
add(1,2,3,4,5,6,7,8);
add(2,3,2,4,6,7,6,8);
add(4,5,4,6,4,7,4,8);
}
void solve4(){
add(1,2,3,4,1145,1145,1145,1145);
add(2,3,2,4,1145,1145,1145,1145);
}
void cond(){
int cnt=n-x;
if(cnt==6){
for(int i=0;i<6;i++)ans.pop_back();
x-=15;
solve11();x+=10;solve11();
}
else if(cnt<=1)return;
else if(cnt<=4)solve4();
else if(cnt<=8)solve8();
else if(cnt<=11)solve11();
else if(cnt<=16)solve16();
}
int main(){
scanf("%d",&n);
for(x=0;x+15<n;x+=15)solve16();
cond();
printf("%d\n",(int)ans.size());
for(node x:ans){
for(int i=0;i<4;i++)printf("%d %d %d ",x.a[i].second,x.a[i].first,x.a[i].second);
puts("");
}
return 0;
}
快踩