protection
题意
给你一个图,只有i和i+1连边,然后还有三对点连边,问m次每对点的最短距离,边的距离都是1 T组数据
T≤5,n≤105,m≤3×105
T
≤
5
,
n
≤
10
5
,
m
≤
3
×
10
5
分析
枚举这三对点使用的情况以及顺序,其实是一个常数,暴力就过了
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e5+10;
const ll Mod = 1e9+7;
inline ll read()
{
ll p=0; ll f=1; char ch=getchar();
while(ch<'0' ||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
ll t; pair<ll,ll> pr[N]; ll sum[N]; bool vis[N]; ll b[N];
ll ans; ll l,r;
void dfs(ll x)
{
ll s = 0; ll lst = l;
for(ll i=1;i<x;i++)
{
ll xx = pr[b[i]].first; ll yy = pr[b[i]].second;
if(abs(xx-lst) > abs(yy-lst)) swap(xx,yy);
s += abs(xx - lst); s++; lst = yy;
}s += abs(r - lst); ans = min(ans,s);
if(x==3+1) return ;
for(ll i=1;i<=3;i++) if(!vis[i]){vis[i] = 1; b[x] = i; dfs(x+1); vis[i] = 0;}
}
int main()
{
freopen("protection.in","r",stdin);
freopen("protection.out","w",stdout);
t = read();
while(t--)
{
ll n = read(); ll m = read();
for(ll i=1;i<=3;i++) pr[i].first = read(),pr[i].second = read();
ll s = 0;
for(ll p=1;p<=m;p++)
{
l = read(); r = read(); ans = INT_MAX;
if(l>r) swap(l,r);
for(ll i=1;i<=3;i++) vis[i] = 0 ; dfs(1);
s = (s + ans * p) % Mod;
}
printf("%lld\n",s);
}
// cout<<(double)(clock())<<endl;
return 0;
}
lonely
题意
T组数据,给你N,表示一排N个格子,都是白的,求出至少有一个黑格子,且每个黑格子至少都有一个黑格子与这个黑格子相邻的方案数
T≤50000,n≤1018
T
≤
50000
,
n
≤
10
1
8
分析
简单的矩阵乘法一下,首先有
g[i]=f[i−1]+1
g
[
i
]
=
f
[
i
−
1
]
+
1
f[i]=f[i−1]+∑i=0i−2g[i]
f
[
i
]
=
f
[
i
−
1
]
+
∑
i
=
0
i
−
2
g
[
i
]
其中
f
f
表示答案,表示最右的一格必须为白格且合法的方案数
这样矩阵乘法是5*5的,还是不行
考虑只要维护这个格子和上一个格子的状态即
f[i][sta]
f
[
i
]
[
s
t
a
]
即可,这样就是4*4的
好像还有3*3的?
其实可以oeis一波没毛病
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 110;
const ll Mod = 998244353;
inline ll read()
{
ll p=0; ll f=1; char ch=getchar();
while(ch<'0' ||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
/*ll f[N][N][N]; ll g[N];
*/
struct node
{
ll a[5][5];
node(){memset(a,0,sizeof(a));}
friend node operator * (node x,node y)
{
node z;
for(ll i=0;i<4;i++) for(ll j=0;j<4;j++) for(ll k=0;k<4;k++)
z.a[i][j] = (z.a[i][j] + x.a[i][k] * y.a[k][j]) % Mod;
return z;
}
friend node operator ^ (node x,ll k)
{
node z; for(ll i=0;i<4;i++) z.a[i][i] = 1;
while(k)
{
if(k&1) z=z*x;
x=x*x; k>>=1;
}return z;
}
}s,p;
int main()
{
freopen("lonely.in","r",stdin);
freopen("lonely.out","w",stdout);
p.a[0][1] = p.a[0][0] = 1;
p.a[1][3] = 1;
p.a[2][0] = p.a[2][1] = 1;
p.a[3][3] = p.a[3][2] = 1;
ll n;
while(scanf("%lld",&n)!=EOF)
{
if(n==0) break;
s.a[0][0] = s.a[0][1] = 1; s.a[0][2] = s.a[0][3] = 0;
node now = p^(n-1);
s = s * now;
printf("%lld\n",(s.a[0][3] + s.a[0][2] + s.a[0][0] - 1) % Mod);
}
// cout << (double) (clock()) << endl;
return 0;
}
meiliangxing
题意
给出一个N,找出一对数(i,j),使得i>j 且i的二进制的1的个数 < j的二进制的1的个数
N≤10300
N
≤
10
300
分析
看到这么大,先考虑数位dp
好像只确定一个数来数位dp,顶格的情况很难算
其实可以确定两个数来dp
dp[i][j][0/1][0/1][0/1]
d
p
[
i
]
[
j
]
[
0
/
1
]
[
0
/
1
]
[
0
/
1
]
表示从最高位起到i位,i和j差多少个1,i和j分别有无顶格,i和j是否相同,然后就做完了
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1510;
const ll Mod = 998244353;
inline ll read()
{
ll p=0; ll f=1; char ch=getchar();
while(ch<'0' ||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
char ss[N];
ll len;
ll Divide(ll *a)
{
ll lst = 0;
for(ll i=len;i>=1;i--)
{
if(lst) a[i] += 10;
if(a[i] & 1) lst = 1; else lst = 0;
a[i]>>=1;
}
while(a[len] == 0 && len) len--;
return lst;
}
ll a[N];
ll b[N]; ll blen = 0;
void upd(ll &x,ll y){x=(x+y)%Mod;}
ll f[2][3010][2][2][2] , now = 1;
int main()
{
freopen("meiliangxing.in","r",stdin);
freopen("meiliangxing.out","w",stdout);
ll t = read();
while(t--)
{
scanf("%s",ss+1); len = strlen(ss+1);
for(ll i=len;i>=1;i--) a[i] = ss[len - i + 1] - '0';
blen = 0; while(len) b[++blen] = Divide(a);
memset(f,0,sizeof(f));
now = 0; f[now][1500][1][1][1] = 1;
for(ll i=blen;i>=1;i--,now = now ^ 1)
{
memset(f[now^1],0,sizeof(f[now^1]));
for(ll p=0;p<=3000;p++)
for(ll op1 = 0;op1 < 2;op1++)
for(ll op2 = 0;op2 < 2;op2++)
for(ll op = 0;op < 2;op++) if(f[now][p][op1][op2][op])
for(ll j = 0;j < 2;j++) for(ll k = 0;k < 2;k++)
{
if(op == 1 && j > k) continue;
if(op1 == 1 && j > b[i]) continue;
if(op2 == 1 && k > b[i]) continue;
ll n = op & (j == k);
ll n1 = op1 & (j==b[i]);
ll n2 = op2 & (k==b[i]);
upd(f[now^1][p + j - k][n1][n2][n] , f[now][p][op1][op2][op]);
//printf("%lld %lld %lld %lld <- %lld %lld %lld %lld\n",p+j-k,n1,n2,n,p,op1,op2,op);
}
}
ll ans = 0;
for(ll p=1501;p<=3000;p++) for(ll op1=0;op1<2;op1++) for(ll op2=0;op2<2;op2++) if(f[now][p][op1][op2][0])
{
upd(ans , f[now][p][op1][op2][0]);
//printf("%lld %lld %lld %lld\n",p,op1,op2,f[now][p][op1][op2][0]);
}
printf("%lld\n",ans);
}
return 0;
}
grancrevasse
题意
给你一个长度为N的0,1串,m个操作,要求满足两种操作:
异或某一位
如果有01这样的组合就取出,可以连续,且询问独立,询问区间的消去后的第k个位置是哪个位置
1≤n,m≤5×105 1 ≤ n , m ≤ 5 × 10 5
分析
解法一般是线段树
取出后肯定是111111111111110000000000000的形式
那么只要合并的时候把左孩子的0和右孩子的1消掉就好
考虑查找,二分查找
O(mlog2n)
O
(
m
l
o
g
2
n
)
,看看有没有更优的复杂度
直接在线段树上查找,分开看看是查找0,还是查找1
(考虑区间合并要取出,不能一起查找)
查找0的话从右端点找,否则从左端点开始找,时间复杂度
O(mlogn)
O
(
m
l
o
g
n
)
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 500010;
inline int read()
{
int p=0; int f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
char ss[N];
int rt,tot,lc[N<<2],rc[N<<2],c0[N<<2],c1[N<<2];
void upd(int u)
{
if(c0[lc[u]] >= c1[rc[u]])
{
c1[u] = c1[lc[u]];
c0[u] = c0[lc[u]] + c0[rc[u]] - c1[rc[u]];
}
else
{
c1[u] = c1[rc[u]] + c1[lc[u]] - c0[lc[u]];
c0[u] = c0[rc[u]];
}
}
int build(int L,int R)
{
int u = ++tot;
if(L<R)
{
int mid=(L+R)>>1;
lc[u] = build(L,mid);
rc[u] = build(mid+1,R);
upd(u);
}
else
{
if(ss[L] == '0') c0[u] = 1,c1[u] = 0;
else c1[u] = 1,c0[u] = 0;
}return u;
}
void chg(int u,int L,int R,int k)
{
if(L==R){if(ss[L] == '0') c0[u] = 1,c1[u] = 0; else c1[u] = 1,c0[u] = 0; return ;}
int mid = (L+R)>>1;
if(k<=mid) chg(lc[u],L,mid,k);
else chg(rc[u],mid+1,R,k);
upd(u);
}
int s0 , s1;
#define MP make_pair
pair<int,int> merge(pair<int,int> x,pair<int,int> y)
{
if(x.second > y.first) return MP(x.first , x.second - y.first + y.second);
else return MP(x.first + y.first - x.second , y.second);
}
pair<int,int> qry(int u,int L,int R,int l,int r)
{
if(L == l && R == r){return MP(c1[u],c0[u]);}
int mid=(L+R)>>1;
if(r<=mid) return qry(lc[u],L,mid,l,r);
else if(l>mid) return qry(rc[u],mid+1,R,l,r);
else return merge(qry(lc[u],L,mid,l,mid),qry(rc[u],mid+1,R,mid+1,r));
}
int find_l(int u,int L,int R,int pos,int k)
{
if(L==R && k==c1[u]) return L;
if(pos==L && c1[u] < k) return R + k - c1[u] + c0[u];
int mid = (L+R)>>1;
if(pos > mid) return find_l(rc[u],mid+1,R,pos,k);
int tmp = find_l(lc[u] , L , mid , pos, k);
if(tmp <= mid) return tmp;
else return find_l(rc[u], mid+1, R, mid+1, tmp - mid );
}
int find_r(int u,int L,int R,int pos,int k)
{
if(L==R && k==c0[u]) return R;
if(pos == R && c0[u] < k) return L - k + c0[u] - c1[u];
int mid = (L+R)>>1;
if(pos <= mid) return find_r(lc[u],L,mid,pos,k);
int tmp = find_r(rc[u],mid+1,R,pos,k);
if(tmp > mid) return tmp;
else return find_r(lc[u],L,mid,mid, mid+1 - tmp);
}
int main()
{
freopen("grancrevasse.in","r",stdin);
freopen("grancrevasse.out","w",stdout);
int n = read() , m = read();
for(int i=1;i<=n;i++) scanf("\n%c",&ss[i]);
tot = 0; rt = build(1,n);
while(m--)
{
int op = read();
if(op==1)
{
int x = read(); ss[x] = (ss[x] == '0') ? '1' : '0'; chg(rt,1,n,x);
}
else
{
int l = read(); int r = read(); int k = read();
s0 = 0; s1 = 0;
pair<int,int> x = qry(rt,1,n,l,r);
s0 = x.first; s1 = x.second;
if(s0 + s1 < k) printf("-1\n");
else
{
if(k <= s0) printf("%d\n",find_l(rt,1,n,l,k));
else printf("%d\n",find_r(rt,1,n,r,s0+s1-k+1));
}
}
}
return 0;
}