A 選取字串
會 \(n^2\)。
直接列舉全部的 \(q,p\) 然後開一個二維的 bitset 去存一個數是否是某個數的前字尾。
選到兩個 \(p,q\) 時把這兩個數的 bitset 與起來,貢獻是 \(\binom{count}{k}\)。
正解就是先用 kmp 去求出來全部的 border,然後用 border 關係建一棵樹,這棵樹上滿足父親是兒子的 border。根節點為空串。
然後就是樹上選取 \(k\) 個點,求 lca 深度平方和。
點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+10,mod=998244353;
int f[N],g[N];
int ppow(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=(res*a)%mod;
a=(a*a)%mod,b>>=1;
}return res;
}
int n,k;
void init()
{
f[0]=g[0]=1;
for(int i=1;i<=n+2;i++) f[i]=(f[i-1]*i)%mod;
g[n+1]=ppow(f[n+1],mod-2);
for(int i=n;i>=1;i--) g[i]=(g[i+1]*(i+1))%mod;
}
char c[N];
int nxt[N],dep[N],sum[N],cnt[N];
inline int getc(int n,int m)
{
if(n<m) return 0;
return f[n]*g[m]%mod*g[n-m]%mod;
}
signed main()
{
// freopen("a2.in","r",stdin);
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>k>>(c+1);
n=strlen(c+1);
init();
// cout<<n<<"\n";
for(int i=2,j=0;i<=n;i++)
{
for(;j>0&&c[i]!=c[j+1];j=nxt[j]);
if(c[i]==c[j+1])
j++;nxt[i]=j;
}
for(int i=1;i<=n;i++)
dep[i]=dep[nxt[i]]+1;
for(int i=0;i<=n;i++)
sum[i]++;
for(int i=n;i>=1;i--)
sum[nxt[i]]=sum[nxt[i]]+sum[i];
for(int i=0;i<=n;i++)
cnt[dep[i]]=(cnt[dep[i]]+getc(sum[i],k))%mod;
int ans=0;
for(int i=0;i<=n;i++)
ans=(ans+1ll*cnt[i]*(2*i+1)%mod)%mod;
printf("%d",ans);
return 0;
}
B 取石子
其實沒看懂題解。然後就粘過來了。。。
問題模型:取石子(NIM)遊戲,要求每個人每次取的石子數不能超過上一個人剛剛取的,第一個人最
開始可以取不超過 \(K\) 個。
考慮策略:
-
如果 \(\sum_i a_i\) 是奇數,先手取 \(1\) 個必勝,因為每個人之後都只能取 \(1\) 個;
-
否則,先手最優一定取偶數個並且後面每個人能取偶數個都只會取偶數個(否則留給對手總和為奇
數的情況,自己必敗),所以可以遞迴到 \(K\gets \lfloor \frac{K}{2} \rfloor , a_i\gets \lfloor \frac{a_i}{2} \rfloor\)。
解得先手必勝當且僅當對於某個 \(t\le \log_2 K\),\(\sum{i=1}^n \lfloor \frac{a_i}{2^t}\rfloor\) 模 \(2\) 和
為 \(1\),也即 \(\mathop{\oplus}i a_i \not\equiv 0\pmod {2^{\lfloor \log_2 K\rfloor}}\)。
定義 \(\mathrm{lowbit}(x)\) 表示整除 \(x\) 的最大的 \(2\) 的冪。先手第一步能必勝的策略必定是取
\((2k+1)\cdot \mathrm{lowbit}(\mathop{\oplus}i a_i)\) 個。列舉取的堆,假設是第 \(i\) 堆,考慮列舉取
完之後另一個人面對的剩下的異或和的 \(\mathrm{lowbit}\),假設是 \(2^t\),那麼先手取的個數為 \(a_i -
(\mathop{\oplus}{j\ne i} a_j)\oplus 2^t \pmod {2^{t+1}}\)。由於必勝,後手不能取到 \(2^t\),於是自己
這次取的個數也必須小於 \(2^t\)。列舉 \(t\) 依次判斷即可。注意處理 \(t=\infty\)(取完之後異或和歸零)
的情況。
我的理解比較淺層,但是算一點:
-
對於和為偶數的情況,所有選取的答案一定為偶數,奇數同理。
-
對於奇數情況,必勝順序一定是第一次先手選一個奇數,然後後手開始選偶數,即上述兩種情況可以化為一起。
-
對於偶數的選取情況,若 \(\displaystyle\sum \lfloor \frac{a_i}{2}\rfloor\) 為偶數,則選取的數一定是 \(4\) 的倍數,否則模 \(4\) 餘 \(2\)。
但是我至今沒有理解為什麼取偶數個都會取偶數個。
點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
int n,k;
inline int lowbit(int x)
{
if(x==0) return 1e9;
return x&-x;
}
const int N=5e4+6;
int *a;
signed main()
{
freopen("nim.in","r",stdin);
freopen("nim.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int sum=0;
cin>>n>>k;
a=new int [n+2];
for(int i=1;i<=n;i++)
{
cin>>a[i],sum^=a[i];
}
if(lowbit(sum)>k) return cout<<0,0;
cout<<"1\n";
for(int i=1;i<=n;i++)
{
int now=0,r=sum^a[i];
while(114)
{
bool flag=0;
for(int j=0;j<=30;j++)
{
if(now+(1<<j)>min(a[i],k)) continue;
int kk=now+(1<<j);
if(lowbit((a[i]-kk)^r)>kk)
{
now=kk,flag=1;break;
}
}
if(!flag) break;
cout<<i<<" "<<now<<"\n";
}
}
}
C 均衡區間
首先,處理所有數左邊第一個大於它的和小於它的數是顯然的。貢獻會加在這兩個點 min 的外面。
然後就有一個跑的極快的假做法,雖然會被單調序列卡到 \(n^2\)。
如下。每次跳最大和最小的位置,然後更新答案。用補集的形式搞,所以不用處理合法的答案。
點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+6;
inline int read()
{
register int s=0;
register char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') s=(s<<1)+(s<<3)+(c^48),c=getchar();
return s;
}
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
int n,id,a[N],ans[2][N],mx[N],mi[N],st1[N],st2[N],top1,top2,b[N];
signed main()
{
freopen("interval.in","r",stdin);
freopen("interval.out","w",stdout);
n=read(),id=read();
if(id==2)
{
for(int i=1;i<=n;i++) putchar('0'),putchar(' ');
putchar(10);
for(int i=1;i<=n;i++) putchar('0'),putchar(' ');
putchar(10);return 0;
}
for(int i=1;i<=n;i++) a[i]=read(),ans[0][i]=n-i+1,ans[1][i]=i;
for(int i=n;i;i--)
{
while(top1&&a[st1[top1]]<a[i]) mx[st1[top1--]]=i;
while(top2&&a[st2[top2]]>a[i]) mi[st2[top2--]]=i;
st1[++top1]=i,st2[++top2]=i;
}
for(int i=1;i<=n;i++)
{
int j=min(mx[i],mi[i]);
if(!j)j++;
ans[1][i]-=i-j+1;
--b[j],++b[i+1];
int k=mx[i];
while(k>=j) k=mx[k];
while(k) ans[1][i]--,ans[0][k]--,k=mx[k];
k=mi[i];
while(k>=j) k=mi[k];
while(k) ans[1][i]--,ans[0][k]--,k=mi[k];
}
for(int i=1;i<=n;i++)
{
b[i]+=b[i-1];
write(ans[0][i]+b[i]),putchar(' ');
}
putchar(10);
for(int i=1;i<=n;i++)
{
write(ans[1][i]),putchar(' ');
}
}
正解其實就是把上面的處理看作二維數點問題,用樹狀陣列解決。
程式碼來自 why
#include<bits/stdc++.h>
#define N 1000005
#define pb push_back
using namespace std;
int n,id;
int a[N];
int L[N],R[N],stk[N],tot;
vector<int> s[N],t[N];
struct BIT
{
int c[N];
void add(int x,int y) {while(x <= n){c[x] += y; x += (x & (-x));}}
int sum(int x)
{
int res = 0;
while(x) {res += c[x];x -= (x & (-x));}
return res;
}
void cl() {memset(c,0,sizeof(c));}
}B;
int main()
{
freopen("interval.in","r",stdin);
freopen("interval.out","w",stdout);
scanf("%d%d",&n,&id);
for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
for(int i = 1;i <= n;i++)
{
while(tot && a[i] <= a[stk[tot]]) tot--;
L[i] = stk[tot] + 1;
stk[++tot] = i;
}
tot = 0;
for(int i = 1;i <= n;i++)
{
while(tot && a[i] >= a[stk[tot]]) tot--;
L[i] = min(L[i],stk[tot] + 1);
stk[++tot] = i;
}
tot = 0;stk[tot] = n + 1;
for(int i = n;i >= 1;i--)
{
while(tot && a[i] >= a[stk[tot]]) tot--;
R[i] = stk[tot] - 1;
stk[++tot] = i;
}
tot = 0;stk[tot] = n + 1;
for(int i = n;i >= 1;i--)
{
while(tot && a[i] <= a[stk[tot]]) tot--;
R[i] = max(R[i],stk[tot] - 1);
stk[++tot] = i;
}
// for(int i = 1;i <= n;i++) printf("%d %d\n",L[i],R[i]);
int res = 0;
for(int i = 1;i <= n;i++) s[L[i]].pb(i),t[i].pb(i);
for(int i = 1;i <= n;i++)
{
for(int j : s[i]) B.add(j,1),res++;
int buok = res - (B.sum(R[i]) - B.sum(i - 1));//總數減去合法部分
int ans = (n - R[i]) - buok;
printf("%d ",ans);
for(int j : t[i]) B.add(j,-1),res--;
}
printf("\n");
res = 0;
B.cl();
for(int i = 1;i <= n;i++) s[i].clear(),t[i].clear();
for(int i = 1;i <= n;i++) s[i].pb(i),t[R[i]].pb(i);
for(int i = 1;i <= n;i++)
{
for(int j : s[i]) B.add(j,1),res++;
int buok = res - (B.sum(i) - B.sum(L[i] - 1));
int ans = (L[i] - 1) - buok;
printf("%d ",ans);
for(int j : t[i]) B.add(j,-1),res--;
}
return 0;
}