1. 程式人生 > 實用技巧 >聯考20200805 T1

聯考20200805 T1


分析:
考場上寫一個最劣會被卡到\(O(n^2k!k)\)的做法,沒加任何剪枝只想撈50分,竟然過了??
最後兩個點936ms,感謝不殺之恩。。。
首先我們發現一個右端點對應的左端點單調不降,於是two pointer
中間的值我是暴力判斷是否合法的,正解在這裡有了優化

把排列的所有方案都表示出來,一共有326種狀態,在這些狀態之中進行轉移
複雜度\(O(nk!k)\)
寫起來太精汙了,直接上暴力(

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>

#define maxn 100005

using namespace std;

inline int getint()
{
	int num=0,flag=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
	return num*flag;
}

int n,K;
int a[maxn][5];
int L[maxn],P[maxn],vis[maxn];
long long ans;

inline bool check(int l,int r)
{
	int A[5],cnt=0;
	for(int i=l;i<=r;i++)
	{
		bool p=0;
		for(int j=0;j<cnt;j++)if(a[i][P[j]]==A[j]){p=1;break;}
		if(!p)
		{
			if(cnt==K)return 0;
			A[cnt]=a[i][P[cnt]],cnt++;
		}
	}
	return 1;
}

inline void solve()
{
	int now=1;
	for(int i=1;i<=n;i++)
	{
		while(now<i&&!check(now,i))now++;
		L[i]=min(L[i],now);
	}
}

inline void dfs(int x)
{
	if(x==K){solve();return;}
	for(int i=0;i<K;i++)if(!vis[i])
		vis[i]=1,P[x]=i,dfs(x+1),vis[i]=0;
}

int main()
{
	n=getint(),K=getint();
	for(int i=1;i<=n;i++)for(int j=0;j<K;j++)a[i][j]=getint();
	for(int i=1;i<=n;i++)L[i]=i;
	if(K==1)
	{
		for(int i=2;i<=n;i++)if(a[i][0]==a[i-1][0])L[i]=L[i-1];
		for(int i=1;i<=n;i++)ans+=1ll*(i-L[i]+1)*(i-L[i]+2)/2;
		printf("%lld\n",ans);
		return 0;
	}
	dfs(0);
	for(int i=1;i<=n;i++)ans+=1ll*(i-L[i]+1)*(i-L[i]+2)/2;
	printf("%lld\n",ans);
}