题目链接:Comet OJ
A.迫真字符串
#include<bits/stdc++.h>
using namespace std;
const int mx = 2e5+7;
int a[20],n;
char s[mx];
int main(){
scanf("%s",s);
for(int i=0;s[i];i++)
a[s[i]-'0']++;
int ans = min(a[1]/3,a[5]);
ans = min(ans,a[4]/2);
printf("%d\n",ans);
return 0;
}
B.迫真数论
实际上也就是17,18可以。
C.迫真小游戏
对ai排个序,从小到大,然后再将深度从小到大排个序,枚举一个个塞进优先队列中,然后贪心就ok了。赛时没有标记已经被选过的点,一直wa,后来想想没有标记确实有可能出错。
#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pa;
const int mx = 5e5+7;
int n,a[mx],dep[mx],ans[mx],sz;
vector <int> g[mx];
pa b[mx],c[mx];
bool vis[mx];
priority_queue <int,vector<int>,greater<int>> q;
void dfs(int u,int fa,int d){
dep[u] = d;
for(int v:g[u]) if(v!=fa)
dfs(v,u,d+1);
}
int main(){
scanf("%d",&n);
int u,v,now;
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1,0,1);
for(int i=1;i<=n;i++){
scanf("%d",&u);
b[i].fi = u,b[i].se = i;
c[i].fi = dep[i],c[i].se = i;
}
sort(b+1,b+1+n);
sort(c+1,c+1+n);
for(int i=1,j=1;i<=n;i++){
v = b[i].se;
if(vis[v]) continue;
while(j<=n&&c[j].fi<=b[i].fi) q.push(c[j].se),j++;
while(!q.empty()){
int now = q.top();
if(now<=v){
ans[sz++] = now;
vis[now] = 1;
q.pop();
}else break;
}
}
for(int i=0;i<n;i++)
printf("%d%c",ans[i],i==n-1?'\n':' ');
return 0;
}
D.迫真图论
对于一个边的权值我们可以考虑把它放到其中一个端点中,对于端点可以采用一个分块,将点的度为<S和>=S两种,设S>=sqrt(m)。
1.若一条边的端点有一个点的度>=S,那么就将这个边的值放到其中一个度较大的当中,但是当另一个端点值被改变时这个边的转化下标也会受到影响,所以对于另一个端点要记录没放在它里面的边。
2.若两个边的度都<S,那么都不放在端点里面考虑,直接用树状数组更新就好了。然后将这个边都记录到两端,因为这个边都没有被放入。
所以对于改变一个节点的权值,只需要去暴力更新该节点记录的没被放在该节点的边。
一个节点最多存下<S个边,因为当这个节点的度<S时,并且跟他相邻的点的度也<S时,因为度>=S的点最多m/S<=sqrt(m)
对于不存放在端点的边的暴力更新,可以采用分块法,那样更新时O(1),查询是sqrt(n),不用树状数组是因为查询对多q次,但是对于更新每个节点如果都是S个边,对于另一端点度<S的边来说,更新时O(1),对于另一端点度>=S来说更新时O(logn),一个节点最多存在度>=S的点是m/S个,所以最坏情况是q*max(sqrt(n),m/S*log(n),S)
最后就是放在端点里的边了,度>=S的点最多是m/S,对于每个点我们可以去做一个树状数组,维护边的另一个端点的值得前缀和个数。对于端点值为b,然后我要查询和b异或<=x的前缀和,这个可以枚举二进制,最多分成log(n)的连续的段,所以可以O(log^2(n))查询。所以对于查询最坏的情况是O(q*m/S*log^2(n)),
所以S的选取也是很关键的,当S取到sqrt(m)*log(m)时,可以使得复杂度更低。时间复杂度可以优化到O(q*sqrt(m)*log(n))
#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pa;
const int N = 131072;
const int mod = 998244353;
const int mx = N+10;
const int B = 400;
const int limt = 6000;
int n,m,Q,b[mx],id[mx];
int du[mx],q[5555],hd;
int be[mx];
ll s[80][mx],s1[mx],s2[mx];
vector <int> g[mx];
struct node{
int x,y;
int c;
}e[mx];
void add(int o,int x,int v){
if(!x) { s[o][0] += v; return ;}
for(;x<N;x+=x&-x)
s[o][x] += v;
}
void add(int x,int v){
s1[x] += v;
s2[x/B] += v;
}
void upt(int x,int v){
node rt = e[x];
if(be[x]){
int o = be[x];
add(o,b[rt.x]^b[rt.y]^b[q[o]],v*rt.c);
}else add(b[rt.x]^b[rt.y],v*rt.c);
}
ll ask(int x){
ll ans = 0;
int o = x / B;
for(int i=0;i<o;i++) ans += s2[i];
for(int i=x/B*B;i<=x;i++) ans += s1[i];
return ans;
}
ll ask(int o,int x){
if(x==-1) return 0;
ll ans = s[o][0];
for(;x;x-=x&-x) ans += s[o][x];
return ans;
}
ll ask(int o,int l,int r){
return ask(o,r) - ask(o,l-1);
}
ll query(int x){
if(x==-1) return 0;
ll ans = ask(x);
for(int i=1;i<=hd;i++){
int o = b[q[i]],L = 0;
for(int j=16;j>=0;j--){
if(x>>j&1){
if(o>>j&1)
ans += ask(i,L+(1<<j),L+(1<<j+1)-1);
else
ans += ask(i,L,L+(1<<j)-1),L += (1<<j);
}else if(o>>j&1) L += (1<<j);
}
ans += ask(i,L,L);
}
return ans;
}
int main(){
scanf("%d%d%d",&n,&m,&Q);
for(int i=1;i<=n;i++) scanf("%d",b+i);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].c);
du[e[i].x]++,du[e[i].y]++;
}
for(int i=1;i<=n;i++)if(du[i]>=limt){
hd++,q[hd] = i;
id[i] = hd;
}
for(int i=1;i<=m;i++){
int L = e[i].x,R = e[i].y;
if(id[L]){
be[i] = id[L];
g[R].push_back(i);
}else if(id[R]){
be[i] = id[R];
g[L].push_back(i);
}else{
g[L].push_back(i);
g[R].push_back(i);
}
upt(i,1);
}
int u,v,c;
for(int i=1;i<=Q;i++){
scanf("%d%d%d",&c,&u,&v);
if(c==1){
for(int j:g[u]) upt(j,-1);
b[u] = v;
for(int j:g[u]) upt(j,1);
}else if(c==2){
upt(u,-1);
e[u].c = v;
upt(u,1);
}else{
printf("%lld\n",(query(v)-query(u-1))%mod);
}
}
return 0;
}
E.迫真大游戏
p为一个点消失的概率,那么q = 1-p就是没有消失的概率
令
表示n个点并且是第一个点最后一个消失的概率,我们可以枚举第一圈小时了多少个点,那么有:
这个可以用分治fft求出
,O(n*log(n)*log(n))另外令
表示n个点然后第i个点最后一个消失的概率,那么有:
然后这两个还是可以fft,O(n*log(n))
我们再看下求fn能不能优化,将问题转化为n个点,每次每个点都做一次判定,有p的概率被标记,被标记之后下次还可以再标记,请问点被最后一个标记的概率,然后我们枚举这个点在第i+1被标记的概率,那么有:
后面二项式展开:
有当x<1时1+x+x^2+x^3+x^4+...+x^n = 1/(1-x),得
然后这个组合数分开,然后就可以用一个fft求出来了。
分治做法:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
const int g = 3;
const int len = 19;
const int mx = 1<<len;
int n,x,y,rev[mx];
ll fac[mx],inv[mx],p,q;
ll g1[mx],g2[mx],c[mx];
ll a[mx],f[mx],b[mx];
void get_rev(int len)
{
for(int i=1;i<(1<<len);i++)
rev[i] = (rev[i>>1]>>1)|((i&1)<<(len-1));
}
ll qpow(ll x,ll y){
ll ans = 1;
while(y){
if(y&1) ans = ans*x%mod;
y >>= 1;
x = x*x%mod;
}
return ans;
}
void init(int x,int y)
{
int _g = __gcd(x,y);
x /= _g,y /= _g;
p = x*qpow(y,mod-2)%mod,q = (1-p+mod)%mod;
g1[0] = g2[0] = 1;
for(int i=1;i<mx;i++)
g1[i] = p*g1[i-1]%mod,g2[i] = q*g2[i-1]%mod;
f[1] = inv[0] = fac[0] = 1;
inv[1] = fac[1] = 1;
for(int i=2;i<mx;i++){
fac[i] = fac[i-1]*i%mod;
inv[i] = (mod-mod/i)*inv[mod%i]%mod;
}
for(int i=2;i<mx;i++) inv[i] = inv[i]*inv[i-1]%mod;
}
void ntt(ll *p,int len,int v)
{
for(int i=0;i<len;i++)
if(i<rev[i]) swap(p[i],p[rev[i]]);
for(int i=1;i<len;i<<=1){
ll tep = qpow(g,(mod-1)/(2*i));
if(v<0) tep = qpow(tep,mod-2);
for(int j=0;j<len;j+=(i<<1)){
ll mul = 1;
for(int k=j;k<i+j;k++){
ll x = p[k];
ll y = mul*p[k+i]%mod;
p[k] = (x + y)%mod;
p[k+i] = (x - y + mod)%mod;
mul = mul*tep%mod;
}
}
}
if(v==-1){
ll invs = qpow(len,mod-2);
for(int i=0;i<len;i++) p[i] = p[i]*invs%mod;
}
}
void cdq(int l,int r,int sz){
if(l==r) return ;
int mid = l+r>>1,len = (r-l+1);
cdq(l,mid,sz-1);
for(int i=0;i<len/2;i++)
a[i] = g2[i+l]*f[i+l]%mod*inv[i+l-1]%mod;
for(int i=len/2;i<2*len;i++) a[i] = 0;
b[0] = 0;
for(int i=1;i<len;i++) b[i] = g1[i]*inv[i]%mod;
for(int i=len;i<2*len;i++) b[i] = 0;
get_rev(sz);
ntt(a,1<<sz,1);ntt(b,1<<sz,1);
for(int i=0;i<(1<<sz);i++) a[i] = a[i]*b[i]%mod;
ntt(a,1<<sz,-1);
for(int i=mid+1;i<=r;i++)
f[i] = (f[i]+c[i]*a[i-l])%mod;
cdq(mid+1,r,sz-1);
}
int main(){
int x,y;
scanf("%d",&n);
scanf("%d%d",&x,&y);
init(x,y);
for(int i=2;i<=n;i++)
c[i] = fac[i-1]%mod*qpow((1-qpow(q,i)+mod)%mod,mod-2)%mod;
cdq(1,1<<(len-1),len);
for(int i=0;i<(1<<len);i++) {
if(i<n) a[i] = g1[i]*inv[i]%mod*f[n-i]%mod;
else a[i] = 0;
}
for(int i=0;i<(1<<len);i++)
b[i] = i<n?g2[i]*inv[i]%mod:0;
get_rev(len);
ntt(a,1<<len,1);ntt(b,1<<len,1);
for(int i=0;i<(1<<len);i++) a[i] = a[i]*b[i]%mod;
ntt(a,1<<len,-1);
for(int i=1;i<=n;i++) printf("%lld\n",a[i-1]*fac[i-1]%mod);
return 0;
}
一个fft做法:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
const int g = 3;
const int len = 19;
const int mx = 1<<len;
int n,x,y,rev[mx];
ll fac[mx],inv[mx],p,q;
ll g1[mx],g2[mx],c[mx];
ll a[mx],f[mx],b[mx];
void get_rev(int len)
{
for(int i=1;i<(1<<len);i++)
rev[i] = (rev[i>>1]>>1)|((i&1)<<(len-1));
}
ll qpow(ll x,ll y){
ll ans = 1;
while(y){
if(y&1) ans = ans*x%mod;
y >>= 1;
x = x*x%mod;
}
return ans;
}
void init(int x,int y)
{
int _g = __gcd(x,y);
x /= _g,y /= _g;
p = x*qpow(y,mod-2)%mod,q = (1-p+mod)%mod;
g1[0] = g2[0] = 1;
for(int i=1;i<mx;i++)
g1[i] = p*g1[i-1]%mod,g2[i] = q*g2[i-1]%mod;
f[1] = inv[0] = fac[0] = 1;
inv[1] = fac[1] = 1;
for(int i=2;i<mx;i++){
fac[i] = fac[i-1]*i%mod;
inv[i] = (mod-mod/i)*inv[mod%i]%mod;
}
for(int i=2;i<mx;i++) inv[i] = inv[i]*inv[i-1]%mod;
}
void ntt(ll *p,int len,int v)
{
for(int i=0;i<len;i++)
if(i<rev[i]) swap(p[i],p[rev[i]]);
for(int i=1;i<len;i<<=1){
ll tep = qpow(g,(mod-1)/(2*i));
if(v<0) tep = qpow(tep,mod-2);
for(int j=0;j<len;j+=(i<<1)){
ll mul = 1;
for(int k=j;k<i+j;k++){
ll x = p[k];
ll y = mul*p[k+i]%mod;
p[k] = (x + y)%mod;
p[k+i] = (x - y + mod)%mod;
mul = mul*tep%mod;
}
}
}
if(v==-1){
ll invs = qpow(len,mod-2);
for(int i=0;i<len;i++) p[i] = p[i]*invs%mod;
}
}
void solve(int len){
get_rev(len);
ntt(a,1<<len,1);ntt(b,1<<len,1);
for(int i=0;i<(1<<len);i++) a[i] = a[i]*b[i]%mod;
ntt(a,1<<len,-1);
}
int main(){
int x,y;
scanf("%d",&n);
scanf("%d%d",&x,&y);
init(x,y);
for(int i=0;i<(1<<len);i++){
if(i<n){
a[i] = inv[i]*qpow(1-qpow(q,i+1)+mod,mod-2)%mod;
if(i&1) a[i] = mod - a[i];
}else a[i] = 0;
}
for(int i=0;i<(1<<len);i++)
b[i] = i<n?inv[i]:0;
solve(len);
for(int i=1;i<=n;i++) f[i] = a[i-1]*fac[i-1]%mod*p%mod;
for(int i=0;i<(1<<len);i++)
a[i] = i<n?g1[i]*inv[i]%mod*f[n-i]%mod:0;
for(int i=0;i<(1<<len);i++)
b[i] = i<n?g2[i]*inv[i]%mod:0;
solve(len);
for(int i=1;i<=n;i++) printf("%lld\n",a[i-1]*fac[i-1]%mod);
return 0;
}