2024杭电第一场

1003 树

正解是开权值线段树暴力合并

那线段树合并的思想是什么?线段树合并就是线段树_合并啊!(bushi)

想象有两棵线段树,合并时就是对应的节点信息融合

比如现在区间[1,5],用num[rt]表示rt管辖的区间里数的个数

树a的num[rt]=3,树b的num[rt]=4,合并就是加起来=7

时间复杂度一般认为是nlog(n)的,1e5的数据能过

写法的话主要是merge函数和add函数,其他的pushup都是线段树常规操作

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
const int B = 20;
#define mid ((l+r)>>1)
#define ull unsigned long long
int a[N],n,m;
int root[N*B],tot=0;
int num[N*B],ls[N*B],rs[N*B];
ull sum[N*B],sum2[N*B],val[N*B],ans[N*B];
vector<int>e[N];
void pushup(int t){
    num[t]=num[ls[t]]+num[rs[t]];
    sum[t]=sum[ls[t]]+sum[rs[t]];
    sum2[t]=sum2[ls[t]]+sum2[rs[t]];
    val[t]=num[ls[t]]*sum2[rs[t]]+val[ls[t]]+val[rs[t]]-sum[ls[t]]*sum[rs[t]];
    
}
void add(int &t,int l,int r,int x){
    if(!t) t=++tot;
    if(l==r){
        num[t]++;
        sum[t]=(ull)x*num[t];
        sum2[t]=(ull)x*x*num[t];
        return;
    }
    if(x<=mid) add(ls[t],l,mid,x);
    else add(rs[t],mid+1,r,x);
    pushup(t);
}
int merge(int x,int y,int l,int r){
    if(!x || !y) return x+y;
    if(l==r){
        num[x]+=num[y];
        sum[x]+=sum[y];
        sum2[x]+=sum2[y];
        return x;
    }
    ls[x]=merge(ls[x],ls[y],l,mid);
    rs[x]=merge(rs[x],rs[y],mid+1,r);
    pushup(x);
    return x;
}
void dfs(int u,int pre){
    add(root[u],1,N,a[u]);
    for(auto v:e[u]){
        if(v==pre) continue;
        dfs(v,u);
        merge(root[u],root[v],1,N);
    }
    ans[u]=val[root[u]];
}
void solve(){
    cin>>n;
    for(int i=1;i<=n-1;i++){
        int u,v;cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    for(int i=1;i<=n;i++) cin>>a[i];
    //cout<<"6"<<"\n";
    dfs(1,0);
    ull res=0;
    for(int i=1;i<=n;i++) ans[i]*=2;
    for(int i=1;i<=n;i++) res^=ans[i];
    cout<<res<<"\n";
}
int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
}

 

1012 并

考虑两维坐标都离散化后枚举每个格子,用二维前缀和计算其被覆盖的次数,设为cnt

则k固定时该格子的贡献为:( 1 - C[n-cnt][k] / C[n][k] ) * 该格子的面积

注意事项:1.处处取模 2.给的是点坐标,不是格点坐标,因此计算二维前缀和时要x++,y++

#include<bits/stdc++.h>
using namespace std;
const int N = 4e3+5;
const int mod = 998244353;
typedef long long ll;
int n;
int sum[N][N],tot[N];
ll f[N][N];
map<int,int>xid,yid;
struct tringle{
    int x,y,x2,y2;
    void deal(){
        x=xid[x],x2=xid[x2];
        y=yid[y],y2=yid[y2];
        x++,y++;
        sum[x][y]++;
        sum[x][y2+1]--;
        sum[x2+1][y]--;
        sum[x2+1][y2+1]++;
    }
}t[N];
int x[N*2],y[N*2];
int qpow(ll a,ll b){
    ll ret=1;
    while(b){
        if(b&1) ret=ret*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ret;
}
int inv(int x){
    return qpow(x,mod-2);
}
int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    
    f[0][0]=1;
    for(int i=1;i<N;i++){
        f[i][0]=1;
        for(int j=1;j<=i;j++)
            f[i][j]=(f[i-1][j-1]+f[i-1][j])%mod;
    }
        
    cin>>n;
    int cntx=0,cnty=0;
    for(int i=1;i<=n;i++){
        cin>>t[i].x>>t[i].y>>t[i].x2>>t[i].y2;
        x[++cntx]=t[i].x;x[++cntx]=t[i].x2;
        y[++cnty]=t[i].y;y[++cnty]=t[i].y2;
    }
    sort(x+1,x+cntx+1);
    sort(y+1,y+cnty+1);
    cntx=unique(x+1,x+cntx+1)-(x+1);
    cnty=unique(y+1,y+cnty+1)-(y+1);
    

    for(int i=1;i<=cntx;i++){
        xid[x[i]]=lower_bound(x+1,x+cntx+1,x[i])-(x);
     //   cout<<x[i]<<" "<<xid[x[i]]<<"\n";
    }
    
    for(int i=1;i<=cnty;i++){
        yid[y[i]]=lower_bound(y+1,y+cnty+1,y[i])-(y);
    //    cout<<y[i]<<" "<<yid[y[i]]<<"\n";
    }
    for(int i=1;i<=n;i++)
        t[i].deal();
    
    
    for(int i=1;i<=cntx;i++){
        
        for(int j=1;j<=cnty;j++){
            int tmp=(sum[i-1][j]+sum[i][j-1])%mod;
            tmp=(tmp-sum[i-1][j-1]+mod)%mod;
            sum[i][j]+=tmp;
            sum[i][j]%=mod;
            tot[sum[i][j]]+=1ll*(x[i]-x[i-1])*(y[j]-y[j-1])%mod;
            tot[sum[i][j]]%=mod;
        }
    }
    
    ll ans[n+5]={0};
    for(int k=1;k<=n;k++){
        
        int inv_c_n_k=inv(f[n][k]);
        for(int cnt=1;cnt<=n;cnt++){
            ll tmp=(1-1ll*f[n-cnt][k]*inv_c_n_k%mod+mod)%mod;
            tmp=tmp*tot[cnt]%mod;
            ans[k]=(ans[k]+tmp)%mod;
        }
    }
    for(int i=1;i<=n;i++) cout<<ans[i]<<"\n";
    
}

 

 

1005 博弈

分奇偶考虑。​

偶数:
​有一个奇数字符就是五五开,一定不会平局(想象一下两个人拿到的序列互换,这是对称的)
​如果没有的话就是可能会平局,剩下的再五五开,答案为(1-平局的概率)/2
​奇数:
​a肯定会比b多拿1个
​假设只有1个奇数,且前面相等、奇数在最后拿就是a赢,剩下的情况还是五五开。算出a赢的概率设为p,加上(1-p)/2,答案就是 p + (1-p)/2 得到 (1+p)/2 。
​假设有2个奇数,那就是五五开。
​平局的概率:
​把拿出来的序列当成一个排列,前面为a拿的,后面半段为b拿的。假设每个字母都不一样,则这个排列的总方案数为sum!。
​考虑固定前面的一半,对于每个字母都是抽cnt/2个放在前面,于是乘上C(cnt,cnt/2),再进行排列就是(sum/2)! 。

当前面的排列固定后后面半段也随之固定了,不过后面的字母间还有顺序,对于每个字母再乘上(cnt/2)!。

​排列组合要恶补一下了👉👈

#include<bits/stdc++.h>
using namespace std;
#define int long long 
typedef long long ll;
const int N = 1e7+5;
const int mod = 998244353;

ll fact[N],inv[N];
ll power(ll a, ll b, ll p){
    if(b==0) return 1;
    if(a==0) return 0;
    ll res=1;
    a%=p,b%=p;
    while(b>0){
        if(b&1) res=(1ll*res*a)%p;
        b>>=1;
        a=(1ll*a*a)%p;
    }
    return res;
}
ll _inv(ll a){
    return power(a,mod-2,mod);
}
void pre(){
    fact[0]=1;inv[0]=1;
    for(int i=1;i<N;i++)
        fact[i]=(i*fact[i-1])%mod;
    
    inv[N-1]=_inv(fact[N-1]);
    for(int i=N-2;i>=1;i--)
        inv[i]=inv[i+1]*(i+1)%mod;
}
int C(int n, int r, int p) { 
    if(r>n || r<0) return 0;
    if(n==r) return 1;
    if (r==0) return 1; 
    return (((fact[n]*inv[r]) % p )*inv[n-r])%p;
} 

void solve(){
    int n;cin>>n;
    map<char,int>mp;
    int flag=0;
    int sum=0;
    for(int i=1;i<=n;i++){
        char c;int h;
        cin>>c>>h;
        mp[c]=h;
        flag+=(h&1);
        sum+=h;
    }
    if(sum%2==0){
        if(flag) {
            cout<<_inv(2)<<"\n";
            return ;
        }
        else{
            ll tot=_inv(fact[sum]);
            ll a=1;
            for(char i='a';i<='z';i++){
                if(!mp[i]) continue;
                int cnt=mp[i];
                a=a*C(cnt,cnt/2,mod)%mod;
            }
            for(char i='a';i<='z';i++){
                if(!mp[i]) continue;
                int cnt=mp[i];
                a=a*fact[cnt/2]%mod;
            }
            a=a*fact[sum/2]%mod;
            ll p=a*tot%mod;
            ll out=(1-p+mod)%mod*_inv(2)%mod;
            cout<<out<<"\n";
        }
    }
    else {
        if(flag==1){
            ll a=1;
            sum-=1;
            ll tot=_inv(fact[sum+1]);
            for(char i='a';i<='z';i++){
                if(!mp[i]) continue;
                int cnt=mp[i];
                if(cnt&1) {
                    a=cnt;
                    mp[i]--;
                }
            }
            for(char i='a';i<='z';i++){
                if(!mp[i]) continue;
                int cnt=mp[i];
                a=a*C(cnt,cnt/2,mod)%mod;
            }
            for(char i='a';i<='z';i++){
                if(!mp[i]) continue;
                int cnt=mp[i];
                a=a*fact[cnt/2]%mod;
            }
            a=a*fact[sum/2]%mod;
            ll p=a*tot%mod;
            ll out=(1+p)%mod*_inv(2)%mod;
            cout<<out<<"\n";
        }
        else {
            cout<<_inv(2)<<"\n";
        }
    }
}

signed main(){
    ios_base::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    
    pre();
    int t;cin>>t;
    while(t--){
        //TODO
        solve();
    }
}

 

1001 

处理出每个循环串的哈希值丢到一个map里,在大串中查询[ i-len,i ]的哈希值是否在map中出现过,是则+1

不要被骗去写后缀自动机了,这题空间给的很小

#include<bits/stdc++.h>
using namespace std;
const int N = 1048576*2;
const int base=13331;
typedef unsigned long long ull;
ull z[N],p[N],f[N];
ull get1(int l,int r){
	return z[r]-z[l-1]*p[r-l+1];
}
ull get2(int l,int r){
	return f[r]-f[l-1]*p[r-l+1];
}
void solve(){
	map<ull,int>mp;
	string s,t;cin>>s>>t;
	s=s+s;
	s=" "+s;
	p[0]=1;
	int n=s.length();
	n--;
	for(int i=1; i<=n; i++)
	{
		z[i]=z[i-1]*base-s[i]-'a'+1;
		p[i]=p[i-1]*base;
	}
	
	t=" "+t;
	int m=t.length();
	m--;
	for(int i=1; i<=m; i++)
	{
		f[i]=f[i-1]*base-t[i]-'a'+1;
	}
	
	int ans=0;
	for(int i=1;i<=n/2;i++){
		mp[get1(i,i+n/2-1)]=1;
	}
	for(int j=n/2;j<=m;j++){
		if(mp[get2(j-n/2+1,j)]) ans++;
	}
	cout<<ans<<"\n";
}

int main(){
	ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t;cin>>t;
	while(t--){
		//TODO
		solve();
	}
}

 

1002 

暴力dp就可以

正解是 nsqrt(k) 的做法,考虑把读入的数据随机打乱,则在第i格预计的期望是 k/n*i,在这个范围附近找即可,但步长没看懂为什么是v*sqrt(k)

#include<bits/stdc++.h>
using namespace std;
const int N = 1005;
typedef long long ll;
ll dp[N*4],a[N],b[N],c[N],d[N];

void solve(){
	int n,k;cin>>n>>k;
	for(int i=1;i<=n;i++) {
		cin>>a[i]>>b[i]>>c[i]>>d[i];
	}
	
	dp[0]=0;
	for(int j=1;j<=k;j++) dp[j]=1e16;
//	cout<<666<<"\n";
	for(int i=1;i<=n;i++){
		for(int j=k;j>=1;j--){
	
			if(j>=1) dp[j]=min(dp[j],dp[j-1]+a[i]);
			if(j>=2) dp[j]=min(dp[j],dp[j-2]+b[i]);
			if(j>=3) dp[j]=min(dp[j],dp[j-3]+c[i]);
			if(j>=4) dp[j]=min(dp[j],dp[j-4]+d[i]);
		//		cout<<i<<" "<<j<<" "<<dp[j]<<"\n";
 		}
	}
		
	cout<<dp[k]<<"\n";
}
int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;cin>>t;
	while(t--){
		//TODO
		solve();
	}
}

 

1008 

队友做的

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int K=15,N=1<<K;
int n,k,ans;
inline int read()
{
	int x=0;bool f=1;char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar())f^=(ch=='-');
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
	return f?x:-x;
}
void Kafka()
{
	n=read(),k=read();
	ans=1;
	for(int i=0;i<k;++i)
	{
		int res=0;
		if(n&(1<<i)) res=12;
		else res=4;
		ans*=res;
	}
	cout<<ans<<endl;
}
signed main()
{
	for(int T=read();T--;)Kafka();
    return 0;
}

  

 

posted @ 2024-07-20 14:46  liyishui  阅读(37)  评论(0编辑  收藏  举报