中文字幕精品亚洲无线码二区,国产黄a三级三级三级看三级,亚洲七七久久桃花影院,丰满少妇被猛烈进入,国产小视频在线观看网站

CSP2025 題解

你猜我為什(shen)么不寫游(you)記。

直接貪心即可。

#include<bits/stdc++.h>
#define Debug puts("-------------------------")
using namespace std;
const int N=1e5+5;

inline int read(){
	int w=1,s=0;
	char c=getchar();
	for(;c<'0'||c>'9';w*=(c=='-')?-1:1,c=getchar());
	for(;c>='0'&&c<='9';s=s*10+c-'0',c=getchar());
	return w*s;
}
int T,n,a[N][5],id[N],cnt[5],ans; 
signed main(){
//	freopen("club.in","r",stdin);
//	freopen("club.out","w",stdout);
	T=read();
	while(T--){
		cnt[1]=cnt[2]=cnt[3]=0;
		n=read();
		ans=0;
		for(int i=1;i<=n;i++){
			int maxn=0;
			for(int j=1;j<=3;j++) a[i][j]=read(),maxn=max(maxn,a[i][j]);
			for(int j=1;j<=3;j++){
				if(maxn==a[i][j]){
					id[i]=j,cnt[j]++;
					break;
				}
			}
			ans+=maxn;
		}
		int mx=max({cnt[1],cnt[2],cnt[3]});
		if(mx>n/2){
			int o;
			for(int i=1;i<=3;i++) if(mx==cnt[i]) o=i;
			vector<int> vec;
			for(int i=1;i<=n;i++){
				if(id[i]==o){
					int se=0;
					for(int j=1;j<=3;j++) if(j!=o) se=max(se,a[i][j]);
					vec.push_back(a[i][o]-se);
				}
			}
			sort(vec.begin(),vec.end());
			for(int i=0;i<mx-n/2;i++) ans-=vec[i];
		}
		printf("%d\n",ans);
	}
	return 0;
}

根據 Kruskal 的過程不難發現在加上那 \(O(nk)\) 條邊后原圖的邊只有原最小生成樹上的邊是有用的,因此有用的邊數是 \(O(nk)\) 的。
然后 \(O(2^k)\) 枚舉選擇了哪些新點,然后跑 Kruskal 求出此時的最小生成樹即可。
可以提前在外面排好 \(O(nk)\) 條邊的順序,這樣枚舉之后就不需要再排一遍序了。
\(O(2^knk\alpha(n))\),應該不(bu)是正解,但跑得挺(ting)快的。

code

#include<bits/stdc++.h>
#define Debug puts("-------------------------")
#define LL long long 
using namespace std;
const int N=1e4+100,M=1e6+5;

inline int read(){
	int w=1,s=0;
	char c=getchar();
	for(;c<'0'||c>'9';w*=(c=='-')?-1:1,c=getchar());
	for(;c>='0'&&c<='9';s=s*10+c-'0',c=getchar());
	return w*s;
}
int n,m,k,fa[N],c[15],a[15][N],tot;
bool flag[15];
LL ans=LLONG_MAX;
struct Edge{ int u,v,w,op; } E[M],MST[N];
int get(int x){ return (x==fa[x])?(x):(fa[x]=get(fa[x])); }
bool cmp(Edge x,Edge y){ return x.w<y.w; }
void Kruskal(){
	for(int i=1;i<=n;i++) fa[i]=i;
	sort(E+1,E+m+1,cmp);
	for(int i=1;i<=m;i++){
		int x=E[i].u,y=E[i].v;
		if(get(x)==get(y)) continue;
		MST[++tot]=E[i],fa[get(x)]=get(y);
	}
}
void Init(){
	Kruskal();
	for(int i=1;i<=tot;i++) E[i]=MST[i];
	for(int i=1;i<=k;i++) for(int j=1;j<=n;j++) E[++tot]={n+i,j,a[i][j],i};
	sort(E+1,E+tot+1,cmp);
}
LL work(int S){
	LL res=0;
	for(int i=1;i<=k;i++) (S>>(i-1)&1)?(res+=c[i],flag[i]=true):(flag[i]=false);
	for(int i=1;i<=n+k;i++) fa[i]=i;
	for(int i=1;i<=tot;i++){
		if(!flag[E[i].op]) continue;
		int x=E[i].u,y=E[i].v,w=E[i].w;
		if(get(x)==get(y)) continue;
		fa[get(x)]=get(y),res+=w;
	}
	return res;
}
signed main(){
//	freopen("road.in","r",stdin);
//	freopen("road.out","w",stdout);
	double beg=clock();
	n=read(),m=read(),k=read();
	for(int i=1;i<=m;i++){
		int u=read(),v=read(),w=read();
		E[i]={u,v,w,0};
	}
	for(int i=1;i<=k;i++){
		c[i]=read();
		for(int j=1;j<=n;j++) a[i][j]=read();
	}
	Init();
	flag[0]=true;
	for(int S=0;S<(1<<k);S++) ans=min(ans,work(S));
	printf("%lld\n",ans);
	cerr << fixed << setprecision(3) << (double)(clock()-beg) << endl;
	return 0;
}

先求出詢問串 \(t_1,t_2\) 的 LCP 和 LCS,不妨設 \(t_1=ACB,t_2=ADB\),那顯然如果一對 \((s_1,s_2)\) 合法需要滿足他倆中間不一樣的部分也分別為 \(C,D\),不妨設 \(s_1=A'CB',s_2=A'DB'\),那么合法的另一個條件是 \(B'\)\(B\) 的前綴,\(reverse(A')\)\(reverse(A)\) 的前綴。
因此先用哈希(比如 mapvector)將所有 \(C,D\) 相同的二元組和詢問分到同一類里,對每一類單獨求解。在每一類中,對每個 \((s_1,s_2)\)\((t_1,t_2)\)\(A\) 倒序插入第一棵 Trie,將 \(B\) 正序插入第二棵 Trie,那么合法的條件可以轉化為 \((t_1,t_2)\) 在兩棵 Trie 上對應的節點均在 \((s_1,s_2)\) 對應的節點子樹內,這是經典二維偏序,直接樹狀數組即可。
復雜度 \(O((L1+L2)|\sum| + (n+q)\log L)\)
Tip:題目沒保證 \(|t_1|=|t_2|\),注意特判。

code

#include<bits/stdc++.h>
#define Debug puts("-------------------------")
#define ULL unsigned long long 
#define PII pair<int,int>
#define fi first
#define se second 
#define mk make_pair
using namespace std;
const int N=2e5+5,M=5e6+5,p=13331;

inline int read(){
	int w=1,s=0;
	char c=getchar();
	for(;c<'0'||c>'9';w*=(c=='-')?-1:1,c=getchar());
	for(;c>='0'&&c<='9';s=s*10+c-'0',c=getchar());
	return w*s;
}
ULL mi[M];
int n,q,ans[N],Ls[N],Rs[N],len1[N],Lt[N],Rt[N],len2[N];
string s[N][2],t[N][2];
ULL Hash(string &s,int l,int r){
	ULL H=0;
	for(int i=l;i<=r;i++) H=H*p+s[i];
	return H;
}
map<ULL,vector<PII> > mp;
void init(string &s,string &t,int &len,int &L,int &R,int op,int id){
	len=s.size();
	s=' '+s,t=' '+t;
	L=0,R=len+1;
	for(int j=1;j<=len;j++){
		if(s[j]==t[j]) L=j;
		else break;
	}
	for(int j=len;j>=1;j--){
		if(s[j]==t[j]) R=j;
		else break;
	}		
	if(L==len) return;
	int l=R-L+1;
	mp[Hash(s,L+1,R-1)*mi[l]+Hash(t,L+1,R-1)].push_back({op,id});
} 
namespace Solve{
	struct Trie{
		int ch[M][26],tot,dfn[M],siz[M],num;
		void Init(){
			for(int i=1;i<=tot;i++) memset(ch[i],0,sizeof ch[i]);
			tot=1,num=0;
		}
		int insert(string &s,int l,int r,int o){
			int p=1;
			for(int i=(o?r:l);o?(i>=l):(i<=r);o?(i--):(i++)){
				int c=s[i]-'a';
				if(!ch[p][c]) ch[p][c]=++tot;
				p=ch[p][c];
			}
			return p;
		}
		void dfs(int u){
			dfn[u]=++num,siz[u]=1;
			for(int i=0;i<=25;i++) if(ch[u][i]) dfs(ch[u][i]),siz[u]+=siz[ch[u][i]];
		}
	}T1,T2;
	PII ed1[N],ed2[N];
	struct BIT{
		int c[M];
		void init(int num){ for(int i=1;i<=num;i++)	c[i]=0; }
		int lowbit(int x){ return x&(-x); }
		void add(int i,int x,int num){	for(;i<=num;i+=lowbit(i)) c[i]+=x; }
		int ask(int i,int sum=0){
			for(;i;i-=lowbit(i)) sum+=c[i];
			return sum;
		}
	}Bit;
	struct P{ int op,x,y1,y2; }a[N*3];
	bool cmp(P x,P y){
		if(x.x!=y.x) return x.x<y.x;
		return x.op<y.op;
	}
	void work(vector<PII> vec){
		T1.Init(),T2.Init();
		for(PII o:vec){
			int op=o.fi,id=o.se;
			if(op==0) ed1[id]=mk(T1.insert(s[id][0],1,Ls[id],1),T2.insert(s[id][1],Rs[id],len1[id],0));
			else ed2[id]=mk(T1.insert(t[id][0],1,Lt[id],1),T2.insert(t[id][1],Rt[id],len2[id],0)); 
		}
		T1.dfs(1),T2.dfs(1);
		int cnt=0;
		for(PII o:vec){
			int op=o.fi,id=o.se,u,v;
			if(op==0){
				u=ed1[id].fi,v=ed1[id].se;
				a[++cnt]={0,T1.dfn[u],T2.dfn[v],T2.dfn[v]+T2.siz[v]};
				a[++cnt]={-1,T1.dfn[u]+T1.siz[u],T2.dfn[v],T2.dfn[v]+T2.siz[v]};
			}
			else{
				u=ed2[id].fi,v=ed2[id].se;
				a[++cnt]={id,T1.dfn[u],T2.dfn[v],0};
			}
		}
		sort(a+1,a+cnt+1,cmp);
		int num=T2.num;
		Bit.init(num);
		for(int i=1;i<=cnt;i++){
			int op=a[i].op;
			if(op==0) Bit.add(a[i].y1,1,num),Bit.add(a[i].y2,-1,num);
			else if(op==-1) Bit.add(a[i].y1,-1,num),Bit.add(a[i].y2,1,num);
			else ans[op]=Bit.ask(a[i].y1);
		}
	}
}
signed main(){
//	freopen("replace.in","r",stdin);
//	freopen("replace.out","w",stdout);
	double beg=clock();
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	mi[0]=1;
	for(int i=1;i<M;i++) mi[i]=mi[i-1]*p;
	
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		cin>>s[i][0]>>s[i][1];
		init(s[i][0],s[i][1],len1[i],Ls[i],Rs[i],0,i);
	}
	for(int i=1;i<=q;i++){
		cin>>t[i][0]>>t[i][1];
		if(t[i][0].size()!=t[i][1].size()) continue;
		init(t[i][0],t[i][1],len2[i],Lt[i],Rt[i],1,i);		
	}
	for(pair<ULL,vector<PII> > o:mp) Solve::work(o.se);
	for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);
	cerr << fixed << setprecision(3) << (double)(clock()-beg) << endl;
	return 0;
}

\(cnt_i\) 表示耐心值為 \(i\) 的人數,\(pre\)\(cnt\) 的前綴和。
一個顯然的 DP 狀態是 \(f_{i,j}\) 表示前 \(i\) 個人,有 \(j\) 個人被拒絕了的方案數。但發現這樣是轉移不動的,因為比如當我們需要往第 \(i+1\) 放一個 \(c\le j\) 的人時,由于我們并不知道前 \(i\) 個人具體的耐心值(zhi)情況,所以并(bing)不知道還(huan)可以放的人有多少(shao)。

因此我們不妨加一維 \(f_{i,j,k}\) 其中 \(k\) 表示前 \(i\) 個人中有 \(k\) 個人的耐心值 \(\le j\)。此時我們就知道,如果我要在 \(i+1\) 放一個耐心值 \(\le j\) 的人,那么總共就有 \(pre_j-k\) 的方案了。但是一個新的問題是,當第 \(i+1\) 個人被拒絕時,\(j\) 會變成 \(j+1\),此時我們就需要知道 \(c\le j+1\) 的(de)(de)人數了,但顯然根據我們(men)目前的(de)(de)狀態,這(zhe)個信息是(shi)不(bu)能直接推出(chu)來(lai)的(de)(de)。

解決方法其實也比較套路,我們考慮貢獻延后計算,先不去確定那 \(i-k\)\(c>j\) 的人的具體的耐心值,而是在 \(j\to j+1\) 時,再去分配有哪些人的 \(c=j+1\),具體的(de),考(kao)慮刷表法:

  1. \(s_{i+1}=1\) 且第 \(i+1\) 個人被錄取,選一個 \(c>j\) 的人即可,但先不去確定他是誰:\(f_{i,j,k} \to f_{i+1,j,k}\)
  2. \(s_{i+1}=1\) 且第 \(i+1\) 個人不被錄取,先從 \(pre_j-k\) 個人當中選一個 \(c\le j\) 的人,再枚舉 \(l\) 表示那 \(i-k\) 個人當中有幾個人的耐心值為 \(j+1\)\(f_{i,j,k}\times (pre_j-k) \times \binom{i-k}{l} \times \binom{cnt_{j+1}}{l} \times l! \to f_{i+1,j+1,k+l+1}\)
  3. \(s_{i+1}=0\) 此時無論填什么都會拒絕錄取,因此轉移跟 2 差不多,具體看代碼。

看似狀態數是 \(O(n^3)\),轉移枚舉 \(l\)\(O(n)\) 的,但是注意到 \(l\le cnt_{j+1}\),因此當 \(i,k\) 確定時,對于所有 \(1\le j\le n\)\(l\) 的總枚舉量為 \(O(n)\)。總復雜度為 \(O(n^3)\)

code

#include<bits/stdc++.h>
#define Debug puts("-------------------------")
using namespace std;
const int N=5e2+5,mod=998244353;

inline int read(){
	int w=1,s=0;
	char c=getchar();
	for(;c<'0'||c>'9';w*=(c=='-')?-1:1,c=getchar());
	for(;c>='0'&&c<='9';s=s*10+c-'0',c=getchar());
	return w*s;
}
int n,m,cnt[N],pre[N],fact[N],inv[N],f[N][N][N];
char s[N]; 
int C(int n,int m){ 
	if(n<0||m<0||n<m) return 0;
	return 1ll*fact[n]*inv[m]%mod*inv[n-m]%mod; 
}
void Add(int &x,int y){ (x+=y)%=mod; }
signed main(){
//	freopen("employ.in","r",stdin);
//	freopen("employ.out","w",stdout);
	double beg=clock();
	fact[0]=inv[0]=inv[1]=1;
	for(int i=1;i<N;i++) fact[i]=1ll*fact[i-1]*i%mod;
	for(int i=2;i<N;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=2;i<N;i++) inv[i]=1ll*inv[i-1]*inv[i]%mod;
	
	scanf("%d%d%s",&n,&m,s+1);
	for(int i=1,x;i<=n;i++) scanf("%d",&x),cnt[x]++;
	pre[0]=cnt[0];
	for(int i=1;i<=n;i++) pre[i]=cnt[i]+pre[i-1];
	
	f[0][0][0]=1;
	for(int i=0;i<n;i++){
		for(int j=0;j<=i;j++){
			for(int k=0;k<=min(i,pre[j]);k++){
				if(!f[i][j][k]) continue;
				int val=f[i][j][k];
				if(s[i+1]=='1'){
					Add(f[i+1][j][k],val);  //通過,選一個耐心值 >j 的人 
					if(k<pre[j]){
						for(int l=0;l<=min(cnt[j+1],i-k);l++){  //不通過,選一個耐心值 <=j 的人,并決策之前哪些人的耐心值 =j+1 
							Add(f[i+1][j+1][k+l+1],1ll*val*(pre[j]-k)%mod*C(i-k,l)%mod*C(cnt[j+1],l)%mod*fact[l]%mod);
						}
					}
				} 
				else{ //必定失敗 
					for(int l=0;l<=min(cnt[j+1],i-k);l++){
						Add(f[i+1][j+1][k+l],1ll*val*C(i-k,l)%mod*C(cnt[j+1],l)%mod*fact[l]%mod);  //耐心值 >j+1
						if(k+l<pre[j+1]) Add(f[i+1][j+1][k+l+1],1ll*val*(pre[j+1]-k-l)%mod*C(i-k,l)%mod*C(cnt[j+1],l)%mod*fact[l]%mod);  //耐心值 <=j+1 
					}					
				}
			}
		}
	}
	int ans=0;
	for(int j=0;j<=n-m;j++) Add(ans,1ll*f[n][j][pre[j]]*fact[n-pre[j]]%mod);
	printf("%d\n",ans);
	cerr << fixed << setprecision(3) << (double)(clock()-beg) << endl;
	return 0;
}

后記

考場上玩了一整場原神,什么都沒寫出來。不知道場上咋想的,認為 \(dfn_x\le dfn_y\le dfn_x+siz_x-1\) 是二維偏序,結果就變成四維偏序了。然后我的四題分數就被其他人四維偏序了。
希望是給 NOIP 攢 RP 吧。

posted @ 2025-11-04 14:08  Green&White  閱讀(15)  評論(0)    收藏  舉報