ecfinal2020 部分题解

A

//考虑计算最后四位 momo 的方案数,有一个状态数 n*62*62 的 dp,但是每次有用的只有 n*62 个,暴力转移即可
//前面的方案容斥一下就好了 
#include <bits/stdc++.h>
using namespace std;
const int N=1000005;
const int Mod=998244353;
char s[N];int a[N];
int f[65][65][3];
int pre[N][65],suf[65];
int to(char x){
	if (x>='0'&&x<='9') return x-'0';
	if (x>='a'&&x<='z') return 10+x-'a';
	return 36+x-'A';
}
void add(int&x,int y){
	x=(x+y<Mod?x+y:x+y-Mod);
}
int main() {
	scanf ("%s",s+1);int n=strlen(s+1);
	for (int i=1;i<=n;i++) a[i]=to(s[i]);
	for (int i=1;i<=n;i++){
		for (int j=0;j<62;j++) pre[i][j]=pre[i-1][j];
		pre[i][a[i]]++;
	}
	int ans=0;
	for (int i=n;i>=1;i--){
		int x=a[i],y;
		for (y=0;y<62;y++)
			if (x!=y){
				add(f[x][y][0],f[y][x][1]);
				add(f[x][y][1],f[y][x][2]);
				add(f[x][y][2],suf[y]);
			}
		int tot=1ll*(i-1)*(i-2)/2ll%Mod;
		for (int j=0;j<62;j++) add(tot,Mod-1ll*pre[i-1][j]*(pre[i-1][j]-1)/2%Mod);
		for (y=0;y<62;y++){
			int tmp=f[x][y][0];f[x][y][0]=0;
			int dif=(1ll*(i-1-pre[i-1][x])*pre[i-1][x]%Mod+1ll*(i-1-pre[i-1][y])*pre[i-1][y]%Mod)%Mod;
			dif=(dif+Mod-1ll*pre[i-1][x]*pre[i-1][y]%Mod)%Mod;
			dif=(tot-dif+Mod)%Mod;
			add(ans,1ll*tmp*dif%Mod);
		}
		suf[a[i]]++;
	}
	printf ("%d",ans);
	return 0;
}

B

/*
离线,考虑一个矩形如果在时刻 t 内部出现了被删掉的点,那么它的贡献是前缀 [1,t-1]
所以只需要计算有多少个矩形在时刻 t 被删掉,然后做前缀和就是答案
那么枚举上下行边界,计算每个列最小值出现次数,这是经典问题,对左右维护能延申的单调栈
时间复杂度 O(n^2m) 
*/ 
#include <bits/stdc++.h>
using namespace std;
const int N=505;
int a[N][N],mn[N],L[N],R[N],s[N],top=0;
long long ans[N*N];
int main() {
	freopen ("a.in","r",stdin);
	int n,m;scanf ("%d%d",&n,&m);
	for (int i=1;i<=n*m;i++){
		int x,y;scanf ("%d%d",&x,&y);
		a[x][y]=i;
	}
	for (int i=1;i<=n;i++){
		memset (mn,0x3f,sizeof(mn));
		for (int j=i;j<=n;j++){
			for (int k=1;k<=m;k++) L[k]=1,R[k]=m,mn[k]=min(mn[k],a[j][k]);
			top=0;
			for (int k=1;k<=m;k++) {
				while (top&&mn[s[top]]>mn[k]) R[s[top--]]=k-1;
				s[++top]=k;
			}
			top=0;
			for (int k=m;k>=1;k--) {
				while (top&&mn[s[top]]>mn[k]) L[s[top--]]=k+1;
				s[++top]=k;
			}
			for (int k=1;k<=m;k++) ans[mn[k]-1]+=1ll*(k-L[k]+1)*(R[k]-k+1);
		}
	}
	for (int i=n*m-1;i>=0;i--) ans[i]+=ans[i+1];
	for (int i=1;i<=n*m;i++) printf ("%lld\n",ans[i]);
	return 0;
}

C

/*
我们可以确定每个随机的结果是什么
预处理 now[i][j] 表示第 i 次操作时,第 j 位是原种子哪些位 0/1 异或起来的结果 
显然,若一个模数的后 x 位都是 0,那么取模的结果在二进制下就是原数后面的那些位
这样对于所有为 2,4,8,16... 倍数的模数,可以确定一些方程,这样方程的个数在 n=50 时共有 47 个
使用线性基维护这些方程,暴力枚举自由元,直接 check 即可
这样看上去是 2^17*n 的,但是实际上如果不是那个种子,check 几项就会出现问题,远远达不到 n 的复杂度 
*/
#include <bits/stdc++.h>
using namespace std;
const int N=100005;
#define ll unsigned long long
int n;
void Rand(ll&x){x^=x<<13;x^=x>>7;x^=x<<17;}
int a[N],p[N];
int val[N];
bool check(ll x){
	for (int i=1;i<=n;i++){
		Rand(x);
		if (x%i!=val[i]) return false;
	}
	return true;
}
bitset <64> bit[64];
bool ans[64];
void dfs(int x){
	if (x==64){
		ll v=0;for (int i=0;i<64;i++) v|=(1ull<<i)*ans[i];
		if (check(v)) {cout<<v;exit(0);}
		return;
	}
	if (bit[x][x]) {
		bitset <64> st=bit[x];bool f=ans[x];
		for (int i=x-1;i>=0;i--)
			if (bit[x][i]) bit[x]^=bit[i],ans[x]^=ans[i];
		dfs(x+1);bit[x]=st,ans[x]=f;
	}else{
		bit[x][x]=1;
		ans[x]=1;
		dfs(x+1);
		ans[x]=0;
		dfs(x+1);
		bit[x][x]=0;
	}
}
void insert(bitset <64> now,bool v){
	for (int i=63;i>=0;i--)
		if (now[i]){
			if (!bit[i][i]) {bit[i]=now,ans[i]=v;break;}
			else now^=bit[i],v^=ans[i];
		}
}
bitset <64> now[64];
int main() {
	freopen ("a.in","r",stdin);
//	freopen ("a.out","w",stdout);
	scanf ("%d",&n);
	for (int i=1;i<=n;i++){
		scanf ("%d",&a[i]);
		p[a[i]]=i;
	}
	for (int i=n;i>=1;i--){
		val[i]=p[i];
		p[a[i]]=p[i];
		swap(a[i],a[p[i]]);
	}val[1]=1;
	for (int i=1;i<=n;i++) val[i]--;
	for (int i=0;i<64;i++) now[i][i]=1;
	for (int i=1;i<=n;i++){
		for (int j=63;j>=13;j--) now[j]^=now[j-13];
		for (int j=0;j<=63-7;j++) now[j]^=now[j+7];
		for (int j=63;j>=17;j--) now[j]^=now[j-17];
		if (i&1) continue;
		for (int t=0;;t++){
			insert(now[t],(val[i]>>t)&1);
			if (i&(1<<(t+1))) break;
		}
	}
	dfs(0);
	return 0;
}

D

//枚举两条路径的交集,三分分配权值
#include <bits/stdc++.h>
using namespace std;
const int N=5005;
int Head[N],Next[N<<1],Adj[N<<1],tot=0;
void addedge(int u,int v){
	Next[++tot]=Head[u],Head[u]=tot,Adj[tot]=v;
	Next[++tot]=Head[v],Head[v]=tot,Adj[tot]=u;
}
queue <int> Q;
int dis[N][N];
const int INF=10000;
void bfs(int x){
	Q.push(x);dis[x][x]=0;
	while (!Q.empty()){
		int t=Q.front();Q.pop();
		for (int e=Head[t];e;e=Next[e])
			if (dis[x][Adj[e]]==INF)
				dis[x][Adj[e]]=dis[x][t]+1,Q.push(Adj[e]);
	}
}
int n,m,k;
double check(int d1,int d2,int x,int y){
	double sum=0;
	if (d1){
		int p1=1+x/d1,c1=x%d1;
		sum+=1.0*((d1-c1)*1.0/p1+c1*1.0/(p1+1));
	}else sum+=d1;
	if (d2){
		int p2=1+y/d2,c2=y%d2;
		sum+=2.0*((d2-c2)*1.0/p2+c2*1.0/(p2+1));
	}else sum+=d2;
	return sum;
}
double calc(int x,int y){
	int l=0,r=k;
	while ((r-l)>=6){
		int m1=(l+(r-l)/3),m2=(r-(r-l)/3);
		if (check(x,y,m1,k-m1)<check(x,y,m2,k-m2)) r=m2;
		else l=m1;
	}
	double ans=1e9;
	for (int i=l;i<=r;i++) ans=min(ans,check(x,y,i,k-i));
	return ans;
}
int Min[N];
int main (){
	scanf ("%d%d%d",&n,&m,&k);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			dis[i][j]=10000;
	for (int i=1;i<=m;i++){
		int u,v;scanf ("%d%d",&u,&v);
		addedge(u,v);
	}int s1,t1,s2,t2;
	scanf ("%d%d%d%d",&s1,&t1,&s2,&t2);
	for (int i=1;i<=n;i++) bfs(i);
	double ans=calc(dis[s1][t1]+dis[s2][t2],0);
	memset (Min,0x3f,sizeof(Min));
	for (int s=1;s<=n;s++)
		for (int t=s+1;t<=n;t++){
			int d=dis[s1][s]+dis[t][t1]+dis[s2][s]+dis[t][t2];
			d=min(dis[s1][t]+dis[s][t1]+dis[s2][t]+dis[s][t2],d);
			d=min(dis[s1][s]+dis[t][t1]+dis[s2][t]+dis[s][t2],d);
			d=min(dis[s1][t]+dis[s][t1]+dis[s2][s]+dis[t][t2],d);
			Min[dis[s][t]]=min(Min[dis[s][t]],d);
		}
	for (int i=0;i<=n;i++)
		if (Min[i]!=Min[n+1]) ans=min(ans,calc(Min[i],i));
	printf ("%.12f",ans);
	return 0;
}

E

F

#include <bits/stdc++.h>
using namespace std;
const int N=4e5+5;
struct Node{
	int x,y,id;
}a[N];
bool cmp1(Node a,Node b){
	return a.x==b.x?a.y<b.y:a.x<b.x;
}
bool cmp2(Node a,Node b){
	return a.y==b.y?a.x<b.x:a.y<b.y;
}
bool ans1[N],ans2[N];
bool sgn(int x){return x<0?0:1;}
int main(){
	freopen ("a.in","r",stdin);
	int n,m;scanf ("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf ("%d%d",&a[i].x,&a[i].y),a[i].id=i;
	for (int i=1;i<=m;i++) scanf ("%d%d",&a[n+i].x,&a[n+i].y),a[n+i].id=-i;
	sort(a+1,a+n+m+1,cmp1);
	for (int i=2;i<=n+m;i++)
		if (a[i-1].x==a[i].x&&sgn(a[i-1].id)!=sgn(a[i].id)) {
			if (a[i-1].id<0) ans2[-a[i-1].id]=true;else ans1[a[i-1].id]=true;
			if (a[i].id<0) ans2[-a[i].id]=true;else ans1[a[i].id]=true;
		}
	sort(a+1,a+n+m+1,cmp2);
	for (int i=2;i<=n+m;i++)
		if (a[i-1].y==a[i].y&&sgn(a[i-1].id)!=sgn(a[i].id)) {
			if (a[i-1].id<0) ans2[-a[i-1].id]=true;else ans1[a[i-1].id]=true;
			if (a[i].id<0) ans2[-a[i].id]=true;else ans1[a[i].id]=true;
		}
	for (int i=1;i<=n;i++) putchar(ans1[i]+'0');puts("");
	for (int i=1;i<=m;i++) putchar(ans2[i]+'0');
	return 0;
}

G

/*
对每个加进来的 i 考虑, 从上一个 a_i 出现的位置开始产生一段为 1 的贡献
考虑扫描线右端点,对左端点维护线段树
模 2 意义下相当于区间反转,查询所有子区间的答案相当于历史和,线段树维护即可 
*/ 
#include <bits/stdc++.h>
using namespace std;
const int N=500005;
struct Node{
	bool rev;
	int c0,c1;
	int tg0,tg1;
	long long sum;
}t[N<<2];
#define mid ((l+r)>>1)
void pushup(int root){
	t[root].c0=t[root<<1].c0+t[(root<<1)|1].c0;
	t[root].c1=t[root<<1].c1+t[(root<<1)|1].c1;
	t[root].sum=t[root<<1].sum+t[(root<<1)|1].sum;
}
void pushdown(int root){
	if (t[root].rev){
		swap(t[root<<1].c0,t[root<<1].c1);
		swap(t[(root<<1)|1].c0,t[(root<<1)|1].c1);
		swap(t[root<<1].tg0,t[root<<1].tg1);
		swap(t[(root<<1)|1].tg0,t[(root<<1)|1].tg1);
		t[root<<1].rev^=1;
		t[(root<<1)|1].rev^=1;
		t[root].rev=0;
	}
	if (t[root].tg0){
		t[root<<1].tg0+=t[root].tg0;
		t[(root<<1)|1].tg0+=t[root].tg0;
		t[root<<1].sum+=1ll*t[root<<1].c0*t[root].tg0;
		t[(root<<1)|1].sum+=1ll*t[(root<<1)|1].c0*t[root].tg0;
		t[root].tg0=0;
	}
	if (t[root].tg1){
		t[root<<1].tg1+=t[root].tg1;
		t[(root<<1)|1].tg1+=t[root].tg1;
		t[root<<1].sum+=1ll*t[root<<1].c1*t[root].tg1;
		t[(root<<1)|1].sum+=1ll*t[(root<<1)|1].c1*t[root].tg1;
		t[root].tg1=0;
	}
}
void update(int root,int l,int r,int L,int R){
	if (r<L||l>R) return;
	if (L<=l&&r<=R) {
		t[root].rev^=1;
		swap(t[root].c0,t[root].c1);
		swap(t[root].tg0,t[root].tg1);
		return;
	}
	pushdown(root);
	update(root<<1,l,mid,L,R);
	update((root<<1)|1,mid+1,r,L,R);
	pushup(root);
}
long long query(int root,int l,int r,int L,int R){
	if (r<L||l>R) return 0;
	if (L<=l&&r<=R) return t[root].sum;
	pushdown(root);
	return query(root<<1,l,mid,L,R)+query((root<<1)|1,mid+1,r,L,R);
}
void build(int root,int l,int r){
	if (l==r) {t[root].c0=1;return;}
	build(root<<1,l,mid);
	build((root<<1)|1,mid+1,r);
	pushup(root);
}
#undef mid
int a[N],lst[N];
long long ans[N];
struct Query{
	int l,r,id;
}q[N];
bool cmp(Query a,Query b){
	return a.r<b.r;
}
int main() {
	int n;scanf ("%d",&n);
	for (int i=1;i<=n;i++) scanf ("%d",&a[i]);
	int m;scanf ("%d",&m);
	for (int i=1;i<=m;i++){
		scanf ("%d%d",&q[i].l,&q[i].r);
		q[i].id=i;
	}
	sort(q+1,q+m+1,cmp);
	build(1,1,n);
	int now=1;
	for (int i=1;i<=m;i++){
		while (now<=q[i].r){
			update(1,1,n,lst[a[now]]+1,now);
			t[1].sum+=t[1].c1;
			t[1].tg1++;
			lst[a[now]]=now;
			now++;
		}
		ans[q[i].id]=query(1,1,n,q[i].l,q[i].r);
	}
	for (int i=1;i<=m;i++) printf ("%lld\n",ans[i]);
	return 0;
}

H

I

J

K

/*
还有四张牌未知,对方只要选一张连起来,就能抽出顺子,所以只能用顺子打败他
那么枚举对方手上的顺子是什么,判断是否合法即可 
*/
#include <bits/stdc++.h>
using namespace std;
const int N=15;
int c[4][N];
char s[5][2];
int main() {
	int T;scanf ("%d",&T);
	while (T--){
		memset (c,0,sizeof(c));
		cin>>s[0]>>s[1]>>s[2]>>s[3]>>s[4];
		for (int i=0;i<5;i++){
			int t=0;
			if (s[i][0]=='T') t=10;
			else if (s[i][0]=='J') t=11;
			else if (s[i][0]=='Q') t=12;
			else if (s[i][0]=='K') t=13;
			else if (s[i][0]=='A') t=14;
			else t=s[i][0]-'0';
			int p=(s[i][1]=='C'?0:(s[i][1]=='D'?1:(s[i][1]=='H'?2:3)));
			c[p][t]=1;
		}
		int flag=-1;
		for (int i=0;i<4;i++){
			if (c[i][14]&&c[i][2]&&c[i][3]&&c[i][4]&&c[i][5]) {flag=5;break;}
			for (int j=2;j+4<=14;j++)
				if (c[i][j]&&c[i][j+1]&&c[i][j+2]&&c[i][j+3]&&c[i][j+4]){
					flag=j+4;break;
				}
			if (flag!=-1) break;
		}
		if (flag==-1) {puts("check");continue;}
		for (int i=0;i<2;i++){
			int t=0;
			if (s[i][0]=='T') t=10;
			else if (s[i][0]=='J') t=11;
			else if (s[i][0]=='Q') t=12;
			else if (s[i][0]=='K') t=13;
			else if (s[i][0]=='A') t=14;
			else t=s[i][0]-'0';
			int p=(s[i][1]=='C'?0:(s[i][1]=='D'?1:(s[i][1]=='H'?2:3)));
			c[p][t]=-10;
		}
		for (int i=0;i<4;i++){
			for (int j=2;j+4<=14;j++)
				if (c[i][j]+c[i][j+1]+c[i][j+2]+c[i][j+3]+c[i][j+4]>=1)
					if (j+4>flag){flag=-1;break;}
			if (flag==-1) break;
		}
		if (flag==-1) puts("check");
		else puts("allin");
	}
	return 0;
}

L

#include <bits/stdc++.h>
using namespace std;
const int N=1000005;
int p[N],tot=0;bool notp[N];
void getp(){
	for (int i=2;i<=1000;i++){
		if (!notp[i]) p[++tot]=i;
		for (int j=1;p[j]*p[j]<=1000&&j<=tot;j++){
			notp[i*p[j]]=true;
			if (i%p[j]==0) break;
		}
	}
}
const int Mod=1e9+7;
inline int qpow(int a,int b){
	int ans=1;
	while (b){
		if (b&1) ans=1ll*ans*a%Mod;
		a=1ll*a*a%Mod,b>>=1;
	}
	return ans;
}
int val[N];
int main(){
	getp();
	int n;scanf ("%d",&n);
	for (int i=1;i<=n;i++){
		int x;scanf ("%d",&x);
		for (int j=1;p[j]*p[j]<=x&&j<=tot;j++){
			if (x%p[j]==0) {
				int c=0;
				while (x%p[j]==0) x/=p[j],c++;
				val[p[j]]+=(c&1);
			}
		}
		val[x]++;
	}int ans=1;
	for (int i=2;i<=1000000;i++) ans=1ll*ans*qpow(i,min(val[i],n-val[i]))%Mod;
	printf ("%d",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/ehuohz/p/15592196.html