A. Iris and Game on the Tree
题意:给定一棵树,点权为0或1。定义一个叶子的权值为:考虑从根到叶子的这条路径的点权组成的字符
串,权值为其中01作为连续子串出现次数减去10作为连续子串出现次数。定义树的价值为:权值非零
的叶子个数(不包括root节点1)。现在一些点权变成 ?,博弈的两人分别填充,先手最大化,后手最小化树的价值。求最终树的价值。
解法:一条路径计入答案当且仅当叶子和根权值不同。所以只和根和叶子的权值有关。若根的权值确
定,则策略显然;若根的权值不确定,一个思路是看叶子节点填过的0和1哪个多,按自己的目标填;实
际上当叶子节点的0和1个数相同时会出错:先填根的人必然会亏,所以此时先填非根非叶子节点,考虑
其问号个数的奇偶性即可。需要考虑多余的?能让先手交换先后手。
#include<bits/stdc++.h>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=Pre[x];p;p=Next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=next[p])
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,0x3f,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define MEMx(a,b) memset(a,b,sizeof(a));
#define INF (0x3f3f3f3f)
#define F (1000000007)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define vi vector<int>
#define pi pair<int,int>
#define SI(a) ((a).size())
#define Pr(kcase,ans) printf("Case #%d: %lld\n",kcase,ans);
#define PRi(a,n) For(i,n-1) cout<<a[i]<<' '; cout<<a[n]<<endl;
#define PRi2D(a,n,m) For(i,n) { \
For(j,m-1) cout<<a[i][j]<<' ';\
cout<<a[i][m]<<endl; \
}
#pragma comment(linker, "/STACK:102400000,102400000")
#define ALL(x) (x).begin(),(x).end()
#define gmax(a,b) a=max(a,b);
#define gmin(a,b) a=min(a,b);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return ((a-b)%F+F)%F;}
void upd(ll &a,ll b){a=(a%F+b%F)%F;}
inline int read()
{
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
#define MAXN (200000+10)
ll n,p[MAXN];
vi v[MAXN];
int c[MAXN]={}; //0
int t_stamp=0;
int d[MAXN][2];
void dfs(int x,int fa) {
for (auto u:v[x])
if (u!=fa) {
dfs(u,x);
}
}
char s[MAXN];
int main()
{
// freopen("B.in","r",stdin);
// freopen(".out","w",stdout);
int tt=read();
while(tt--) {
n=read();
For(i,n-1) {
int x=read(),y=read();
v[x].pb(y); v[y].pb(x);
}
cin>>(s+1);
int a=0,b=0,c=0;
Fork(i,2,n) if(SI(v[i])==1) {
if(s[i]=='1') ++a;
else if(s[i]=='0') ++b;
else ++c;
}
int p=0;
Fork(i,2,n) if(SI(v[i])!=1) p+=s[i]=='?';
if(p%2==1) {
if(s[1]=='?' && a==b) cout<<a+(c+1)/2<<endl;
else if(s[1]=='?') cout<<max(a,b)+c/2<<endl;
else if(s[1]=='1') cout<<b+(c+1)/2<<endl;
else cout<<a+(c+1)/2<<endl;
}
else {
if(s[1]=='?') cout<<max(a,b)+c/2<<endl;
else if(s[1]=='1') cout<<b+(c+1)/2<<endl;
else cout<<a+(c+1)/2<<endl;
}
For(i,n) v[i].resize(0);
}
return 0;
}
B. Iris and the Tree
#include<bits/stdc++.h>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=Pre[x];p;p=Next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=next[p])
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,0x3f,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define MEMx(a,b) memset(a,b,sizeof(a));
#define INF (0x3f3f3f3f)
#define F (1000000007)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define vi vector<int>
#define pi pair<int,int>
#define SI(a) ((a).size())
#define Pr(kcase,ans) printf("Case #%d: %lld\n",kcase,ans);
#define PRi(a,n) For(i,n-1) cout<<a[i]<<' '; cout<<a[n]<<endl;
#define PRi2D(a,n,m) For(i,n) { \
For(j,m-1) cout<<a[i][j]<<' ';\
cout<<a[i][m]<<endl; \
}
#pragma comment(linker, "/STACK:102400000,102400000")
#define ALL(x) (x).begin(),(x).end()
#define gmax(a,b) a=max(a,b);
#define gmin(a,b) a=min(a,b);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return ((a-b)%F+F)%F;}
void upd(ll &a,ll b){a=(a%F+b%F)%F;}
inline int read()
{
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
#define MAXN (200000+10)
ll n,p[MAXN];
vi v[MAXN];
int c[MAXN]={}; //0
int t_stamp=0;
int d[MAXN][2];
void dfs(int x,int fa) {
++t_stamp;
for (auto u:v[x]){
// x->u
c[t_stamp]++;
d[u][0]=t_stamp;
dfs(u,x);
c[t_stamp]++;
d[u][1]=t_stamp;
}
}
ll x[MAXN],y[MAXN];
int main()
{
freopen("A.in","r",stdin);
// freopen(".out","w",stdout);
int tt=read();
while(tt--) {
n=read();
ll W;cin>>W;
ll ans=0;
Fork(i,2,n) {
p[i]=read();
v[p[i]].pb(i);
}
For(i,n) c[i]=0;
t_stamp=0;
dfs(1,-1);
int z=n;
ll s=0;
For(i,n-1) {
x[i]=read();
cin>>y[i];
s+=y[i];
Rep(j,2) {
int px=d[x[i]][j];
c[px]--;
if(c[px]==0) --z;
}
cout<<s*2 + (W-s) * z;
if(i==n-1) cout<<endl;else cout<<' ';
}
For(i,n) v[i].resize(0);
}
return 0;
}
C. Eri and Expanded Sets
题意:定义一个正整数集合是合法的:你可以任意次取出集合中的正整数 x,y,满足同奇偶性,且
(
x
+
y
)
/
2
(x+y)/2
(x+y)/2
不在集合中,然后把它加入集合。做到无法再做为止,最终集合内的数必须连续。给定序列,求有
多少个子区间内的数组成的集合是合法的。
解法:考虑做差分:操作比较抽象,考虑结束条件:所有差分必须都是奇数(偶数可拆),且都相同
(可合并)。于是得到公差为奇数的等差数列。显然公差为所有差分的gcd除去所有2因子后的答案。
这个gcd等于不排序的邻项 ,显然有单调性,线段树+二分。
#include<bits/stdc++.h>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=next[p])
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,0x3f,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define MEMx(a,b) memset(a,b,sizeof(a));
#define INF (0x3f3f3f3f)
#define F (1000000007)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define vi vector<int>
#define pi pair<int,int>
#define SI(a) ((a).size())
#define Pr(kcase,ans) printf("Case #%d: %lld\n",kcase,ans);
#define PRi(a,n) For(i,n-1) cout<<a[i]<<' '; cout<<a[n]<<endl;
#define PRi2D(a,n,m) For(i,n) { \
For(j,m-1) cout<<a[i][j]<<' ';\
cout<<a[i][m]<<endl; \
}
#pragma comment(linker, "/STACK:102400000,102400000")
#define ALL(x) (x).begin(),(x).end()
#define gmax(a,b) a=max(a,b);
#define gmin(a,b) a=min(a,b);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return ((a-b)%F+F)%F;}
void upd(ll &a,ll b){a=(a%F+b%F)%F;}
inline int read()
{
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
int gcd(int a,int b){
if(!b) return a; return gcd(b,a%b);
}
const int maxn =400000+10;
int sum[maxn<<2],a[maxn];
void pushup(int o) {
sum[o]=gcd(sum[Lson],sum[Rson]);
}
void build(int l,int r,int o) {
if (l==r) {
sum[o]=a[l]; return ;
}
int m=(l+r)>>1;
build(l,m,Lson),build(m+1,r,Rson);
pushup(o);
}
int query(int l,int r,int o,int L,int R) {
if(L<=l && r<=R ) return sum[o];
int m=(l+r)>>1;
int ret=0;
if(L<=m) ret=gcd(ret,query(l,m,Lson,L,R));
if(m<R) ret=gcd(ret,query(m+1,r,Rson,L,R));
return ret;
}
int main()
{
freopen("C.in","r",stdin);
// freopen(".out","w",stdout);
int tt=read();
while(tt--) {
int n=read();
ll An=n;
For(i,n) a[i]=read();
For(i,n-1) a[i]=abs(a[i+1]-a[i]);
build(1,n,1);
For(i,n-1) {
int l=i,r=n-1,ans=-1;
while(l<=r) {
int m=l+r>>1;
int p=query(1,n,1,i,m);
while(p!=0&&p%2==0) p/=2;
if(p==1) ans=m,r=m-1;else l=m+1;
}
if(ans!=-1) An+=n-ans;
// cout<<i<<' '<<ans<<endl;
}
int p=0;
For(i,n-1){
if(a[i]==0) ++p,An+=p;
else p=0;
}
cout<<An<<endl;
}
return 0;
}
D. Iris and Adjacent Products
题意:对于一个序列,你可以做单点修改(任何时刻值域都是[1,k] )或序列重排。定义权值为最小的单
点修改次数使得任意相邻两个数的乘积不超过 。区间查询权值。
。
解法:考虑最优的重排方式,先放最大值,然后最小值,然后次大值等等地交替放。证明考虑各种交换
证明更优性。于是序列合法当且仅当每个i大值和i小值的乘积都不超过 K。
设c(l,r) 表示值域在[l,r]的值的个数。考虑转化:对于每个
i
<
s
q
r
t
(
k
)
i<sqrt(k)
i<sqrt(k) ,都有
c
n
t
(
1
,
i
)
≥
c
n
t
(
n
/
i
,
n
)
cnt(1,i) \ge cnt(n/i,n)
cnt(1,i)≥cnt(n/i,n)
。这个 cnt本质不同只有
2
s
q
r
t
(
k
)
2sqrt(k)
2sqrt(k)个,暴力前缀和求出来,然后列个式子就好了(修改显然是把max 改成1
,这是容易的),需要注意长度为奇数时最中间一项不需要考虑。
离线莫队询问,复杂度
O
(
(
n
+
q
)
n
+
q
k
)
O((n+q)\sqrt{n} + q\sqrt{k})
O((n+q)n+qk)
。
#include<bits/stdc++.h>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=next[p])
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,0x3f,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define MEMx(a,b) memset(a,b,sizeof(a));
#define INF (0x3f3f3f3f)
#define F (1000000007)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define vi vector<int>
#define pi pair<int,int>
#define SI(a) ((a).size())
#define Pr(kcase,ans) printf("Case #%d: %lld\n",kcase,ans);
#define PRi(a,n) For(i,n-1) cout<<a[i]<<' '; cout<<a[n]<<endl;
#define PRi2D(a,n,m) For(i,n) { \
For(j,m-1) cout<<a[i][j]<<' ';\
cout<<a[i][m]<<endl; \
}
#pragma comment(linker, "/STACK:102400000,102400000")
#define ALL(x) (x).begin(),(x).end()
#define gmax(a,b) a=max(a,b);
#define gmin(a,b) a=min(a,b);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return ((a-b)%F+F)%F;}
void upd(ll &a,ll b){a=(a%F+b%F)%F;}
inline int read()
{
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
#define MAXN (100000+10)
#define N (100000)
bitset<100000> f,g;
int b[MAXN],res[MAXN],belong[MAXN],cnt[MAXN]={},cnt2[MAXN]={};
struct node{
int l,r,id;
friend bool operator<(node a,node b) {
if (belong[a.l] ^ belong[b.l] )
return belong[a.l] < belong[b.l];
return a.r<b.r;
}
}a[MAXN];
int main()
{
// freopen("D.in","r",stdin);
// freopen(".out","w",stdout);
int tt=read();
while(tt--) {
int n=read(),m=read();
ll k=read();
ll KS=sqrt(k);
int BS=sqrt(n);
For(i,n)
b[i]=read();
For(i,m){
a[i]=node{read(),read(),i};
}
For(i,n)
belong[i]=(i-1)/BS+1;
sort(a+1,a+m+1);
int l=1,r=0;
For(i,KS) cnt[i]=cnt2[i]=0;
For(i,m){
while(l>a[i].l){
l--;
if(b[l]<=KS) cnt[b[l]]++; else cnt2[k/b[l]]++;
}while(r<a[i].r){
r++;
if(b[r]<=KS) cnt[b[r]]++; else cnt2[k/b[r]]++;
}while(l<a[i].l){
if(b[l]<=KS) cnt[b[l]]--; else cnt2[k/b[l]]--;
l++;
}while(r>a[i].r){
if(b[r]<=KS) cnt[b[r]]--; else cnt2[k/b[r]]--;
r--;
}
int ans=0;
int psmall=0,pbig=0;
For(i,KS) {
if(psmall==0 && pbig==1 && cnt[i]+ cnt2[i]>0) {
psmall++,pbig--;++ans;
}
psmall+=cnt[i];
pbig+=cnt2[i];
int t=min(psmall,pbig);
psmall-=t,pbig-=t;
if(pbig>psmall) {
ans+=pbig/2;
pbig%=2;
}
}
res[a[i].id]=ans;
}
PRi(res,m)
}
return 0;
}