2017 ACM Jordanian Collegiate Programming Contest

A. Chrome Tabs

当$n=1$时答案为$0$,当$k=1$或$k=n$时答案为$1$,否则答案为$2$。

#include<cstdio>
int T,n,k;
int main(){
	freopen("tabs.in","r",stdin);
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&k);
		if(n==1)puts("0");
		else if(k==1||k==n)puts("1");
		else puts("2");
	}
}

  

B. OverCode

按题意模拟即可。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n;
int a[505];
int main()
{
	freopen("overcode.in","r",stdin);
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d", &n);
		for(int i = 1; i <= n; ++i)scanf("%d", &a[i]);
		sort(a + 1, a + n + 1);
		int p = 1;
		int ans = 0;
		while(p <= n - 5)
		{
			if(a[p + 5] - a[p] <= 1000)
			{
				++ans;
				p += 6;
			}
			else p += 1;
		}
		printf("%d\n", ans);
	}
	return 0;
}
/*
【trick&&吐槽】


【题意】


【分析】


【时间复杂度&&优化】


*/

  

C. A message for you!

按题意模拟即可。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n;
char s[20];
int a[20];
bool e[20];
int main()
{
	freopen("scoreboard.in","r",stdin);
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d", &n);
		scanf("%s", s);
		MS(e, 0);
		for(int i = 0; i < n; i ++){
			e[s[i] - 'A' + 1] = 1;
		}
		for(int i = 1; i <= 13; ++i)scanf("%d", &a[i]);
		int ans = 0, tmp = 0;
		for(int i = 1; i <= 13; i ++){
			if(e[i] == 0 && a[i] > tmp){
				tmp = a[i];
				ans = i;
			}
		}
		printf("%c\n", ans + 'A' - 1);
	}
	return 0;
}
/*
【trick&&吐槽】


【题意】


【分析】


【时间复杂度&&优化】


*/

  

D. Test Cases

枚举左端点,往右枚举右端点,同时维护每个数字出现次数的奇偶性。

时间复杂度$O(n^2)$。

#include<cstdio>
int T,n,i,j,odd,ans,v[1000010],a[5555];
int main(){
	freopen("cases.in","r",stdin);
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		for(i=1;i<=n;i++)scanf("%d",&a[i]);
		ans=0;
		for(i=1;i<=n;i++){
			for(j=i;j<=n;j++)v[a[j]]=0;
			odd=0;
			for(j=i;j<=n;j++){
				v[a[j]]?odd--:odd++;
				v[a[j]]^=1;
				if(odd==1)ans++;
			}
		}
		printf("%d\n",ans);
	}
}

  

E. Robot I - Instruction Reduction

按题意模拟即可。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 505, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m, q;
int y, x; char dir;
char s[N][N];
int f[N][N][4]; //real way
const int dy[4] = {-1,0,1,0};
const int dx[4] = {0,1,0,-1};
int a[1010 * 1010];
int Y, X, D;
pair<int,int> ord[1010];
int main()
{
	freopen("reduce.in","r",stdin);
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		char dir;
		scanf("%d%d%d", &n, &m, &q);
		scanf("%d%d %c", &Y, &X, &dir);
		if(dir == 'U')D = 0;
		else if(dir == 'R')D = 1;
		else if(dir == 'D')D = 2;
		else D = 3;
		int sty = Y, stx = X, stdir = D;
		for(int i = 1; i <= n; ++i)
		{
			scanf("%s", s[i] + 1);
		}
		for(int i = 2; i < n; ++i)
		{
			for(int j = 2; j < m; ++j)if(s[i][j] == '.')
			{
				for(int d = 0; d < 4; ++d)
				{
					for(int k = 0; k < 4; ++k)
					{
						int dd = (d + k) % 4;
						if(s[i + dy[dd]][j + dx[dd]] == '.')
						{
							f[i][j][d] = dd;
							break;
						}
					}
				}
			}
		}
		//printf("f:%d\n", f[3][2][1]);
		int g = 0;
		for(int i = 1; i <= q; ++i)
		{
			char op; scanf(" %c", &op);
			if(op == 'R')
			{
				D = (D + 1) % 4;
			}
			else
			{
				int step;
				scanf("%d", &step);
				while(step--)
				{
					D = f[Y][X][D];
					Y += dy[D];
					X += dx[D];
					a[++g] = D;
				}
			}
		}
		Y = sty, X = stx, D = stdir;
		
		//Y X D
		int o = 0;
		for(int i = 1; i <= g; ++i)
		{
			/*
			printf("step = %d, status = %d %d %d, aimdir = %d\n", i, Y, X, D, a[i]);
			printf("f[%d][%d][%d] = %d, a[i] = %d\n", Y, X, D, f[Y][X][D], a[i]);
			getchar();
			*/
			for(int k = 0; k < 4; ++k)
			{
				if(f[Y][X][D] == a[i])
				{
					D = a[i];
					Y += dy[D]; X += dx[D];
					if(o && ord[o].first == 1)
					{
						++ord[o].second;
					}
					else ord[++o] = {1, 1};
					break;
				}
				else
				{
					ord[++o] = {2, -99999999};
					D = (D + 1) % 4;
				}
			}
		}
		printf("%d\n", o);
	}
	return 0;
}
/*
【trick&&吐槽】


【题意】


【分析】


【时间复杂度&&优化】


*/

  

F. Robot II - Colorful Grid

留坑。

 

G. WiFi Password

枚举区间$OR$本质不同的$O(n\log w)$段区间,更新答案即可。

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=100010;
int T,n,lim,i,j,v[N],a[N],l[N],ans;
inline int fun(int a,int b){return a|b;}
int main(){
	freopen("wifi.in","r",stdin);
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&lim);
		ans=0;
		for(i=1;i<=n;i++)scanf("%d",&a[i]);
		for(i=1;i<=n;i++)for(v[i]=a[i],j=l[i]=i;j;j=l[j]-1){
			v[j]=fun(v[j],a[i]);
			while(l[j]>1&&fun(a[i],v[l[j]-1])==fun(a[i],v[j]))l[j]=l[l[j]-1];
			if(v[j]<=lim)ans=max(ans,i-l[j]+1);
		}
		printf("%d\n",ans);
	}
}

  

H. Gas Stations

枚举去掉哪一个加油站,那么最优策略要么是在一开始插入,要么是在末尾插入,要么是将间隔最大的空段劈成两半,维护前$4$大即可快速询问。

时间复杂度$O(n)$。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100010;
int bestside[N];
int T,n,m,ans,i,j,q[N];
int pre[N],nxt[N];
char a[N];
int w[N],cnt;
int now[N],tmp;
inline bool cmp(int x,int y){return x>y;}
inline int cal(int l,int r){
	int dis=r-l-1;
	if(l==0||r==n+1)return dis;
	return (dis+1)/2;
}
inline int calleft(int mark){
	for(int i=2;i<=m;i++)if(mark!=q[i])return q[i]-1;
}
inline int calright(int mark){
	for(int i=m-1;i;i--)if(mark!=q[i])return n-q[i];
}
inline void gao(int l,int r){
	if(l==0||r==n+1)return;
	w[++cnt]=r-l-1;
}
inline void del(int l,int r){
	if(l==0||r==n+1)return;
	int dis=r-l-1;
	int i;
	for(i=1;i<=tmp;i++)if(now[i]==dis)break;
	if(i>tmp)return;
	for(;i<tmp;i++)now[i]=now[i+1];
	tmp--;
}
inline void add(int l,int r){
	if(l==0||r==n+1)return;
	int dis=r-l-1;
	int i;
	now[++tmp]=dis;
	sort(now+1,now+tmp+1,cmp);
	if(tmp>4)tmp--;
}
inline void work(int x){
	int i=pre[x],j=nxt[x];//i x j
	tmp=cnt;
	for(int _=1;_<=cnt;_++)now[_]=w[_];
	del(i,x);
	del(x,j);
	add(i,j);
	int left=calleft(x),right=calright(x);
	int maxgap=0,secgap=0;
	if(tmp>=1)maxgap=now[1];
	if(tmp>=2)secgap=now[2];
	//case 1 add one in left
	ans=min(ans,max(bestside[left],max(right,(maxgap+1)/2)));
	//case 2 add one in right
	ans=min(ans,max(left,max(bestside[right],(maxgap+1)/2)));
	//case 3 add one in maxgap
	ans=min(ans,max(max(left,right),max((maxgap/2+1)/2,(secgap+1)/2)));
}
int main(){
	freopen("stations.in","r",stdin);
	
	for(i=j=1;i<N;i++){
		while(j<i&&max(j-1,(i-j+1)/2)>max(j,(i-(j+1)+1)/2))j++;
		bestside[i]=max(j-1,(i-j+1)/2);
		//i=3 |___+
	}
	
	
	scanf("%d",&T);
	while(T--){
		scanf("%d%s",&n,a+1);
		ans=n;
		q[m=1]=0;
		for(i=1;i<=n;i++)if(a[i]=='+')q[++m]=i;
		q[++m]=n+1;
		
		if(m==3){
			for(i=1;i<=n;i++)ans=min(ans,max(cal(0,i),cal(i,n+1)));
			printf("%d\n",ans);
			continue;
		}
		
		
		pre[0]=0;
		nxt[n+1]=0;
		for(i=1;i<=m;i++){
			if(i>1)pre[q[i]]=q[i-1];
			if(i<m)nxt[q[i]]=q[i+1];
		}
		cnt=0;
		for(i=1;i<m;i++)gao(q[i],q[i+1]);
		sort(w+1,w+cnt+1,cmp);
		cnt=min(cnt,4);
		for(i=2;i<m;i++)work(q[i]);
		printf("%d\n",ans);
	}
}

  

I. Counting Votes

留坑。

 

J. Efficiency Test

将环倍长,设$f[i][j]$表示在$i$点按$j$步长往右会跳到哪里,可以得到一棵树的结构,在树上DFS,询问时二分即可。

常数优化:将失败的父亲合并,同时剔除子树内不含询问的点。

时间复杂度$O(nk+n\log n)$。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=200010,M=55,E=N*M;
int Case,n,m,nn,i,j,cnt,s[N];
int goal[N],ans[N];
int g[E],nxt[E],f[E],q[E],t;
bool vip[E];
char a[N];
inline bool canjump(int x,int y){
	if(x+y>nn)return 0;
	if(a[x+y]=='#')return 0;
	return s[x+y]-s[x-1]<=1;
}
inline void gao(int x){
	//printf("gao %d\n",x);
	//for(int i=2;i<=t;i++)printf("%d %d\n",q[i],w[i]);
	//[2..t]
	int y=goal[x];
	if(q[2]<y){ans[x]=-1;return;}
	int l=3,r=t,mid,o=2;
	while(l<=r){
		mid=(l+r)>>1;
		if(q[mid]>=y)l=(o=mid)+1;else r=mid-1;
	}
	ans[x]=f[t]-f[o];
}
void dfs(int x){
	//printf("dfs %d\n",x);
	if(!vip[x])return;
	int A=x/(m+1);
	q[++t]=A;
	f[t]=f[t-1];
	if(q[t-1]!=A)f[t]++;
	if(A&&A<=n&&x%(m+1)==m)gao(A);
	for(int i=g[x];i;i=nxt[i])dfs(i);
	t--;
}
int main(){
	freopen("jumps.in","r",stdin);
	scanf("%d",&Case);
	while(Case--){
		scanf("%d%d%s",&n,&m,a+1);
		//for(i=1;i<=n;i++)a[i]='.';
		//a[1]='#';
		nn=n*2;
		for(i=1;i<=n;i++)a[i+n]=a[i];
		for(i=1;i<=nn;i++)s[i]=s[i-1]+(a[i]=='#');
		//[2..m] is valid
		cnt=nn*(m+1)+m;
		for(i=0;i<=cnt;i++)g[i]=vip[i]=0;
		for(i=nn;i;i--)if(a[i]=='.'){
			int y=0;
			for(j=2;j<=m;j++){
				int x=i*(m+1)+j;
				if(canjump(i,j))y=(i+j)*(m+1)+j;
				f[x]=y;
				//printf("f[%d][%d]=%d\n",i,j,y);
				//nxt[x]=g[y];
				//g[y]=x;
			}
			int x=i*(m+1)+m;
			while(!vip[x]){
				vip[x]=1;
				x=f[x];
			}
		}
		for(i=1;i<=nn;i++)if(a[i]=='.'){
			for(j=2;j<=m;j++){
				int x=i*(m+1)+j;
				if(!vip[x])continue;
				int y=f[x];
				nxt[x]=g[y];
				g[y]=x;
			}
		}
		for(i=j=1;i<=n;i++){
			while(s[j]-s[i-1]<s[n])j++;
			goal[i]=j;//>=j
			//printf("goal[%d]=%d\n",i,j);
		}
		dfs(0);
		for(i=1;i<=n;i++){
			if(a[i]=='#')printf("0 ");
			else printf("%d ",ans[i]);
		}
		puts("");
	}
}

  

K. Running Threads

考虑费用流建图:

  • $S$向每条记录连边,流量$[0,1]$,费用$0$,表示一组的开始。
  • 记录$i$向记录$j$连边,流量$[0,1]$,费用$0$,表示一组延续。
  • 记录向线程连边,流量$[0,1]$,费用$w$,表示一组的结束。
  • 每条线程向$T$连边,流量$[0,1]$,费用$0$,表示只接受最多一组记录。
  • 每条记录拆点,中间连边,流量$[1,1]$,费用$0$。

那么两问分别对应这个有上下界的网络的最小/最大费用可行流。

#include<cstdio>
typedef long long ll;
const int N=1000,M=1000000;
const ll inf=1LL<<60;
int Case,n,m,i,j,a[N],b[N];
ll sum;
int u[M],v[M],c[M],co[M],nxt[M],t,S,T,SS,TT,l,r,q[M];
int g[N],f[N];
bool vis[N];
ll d[N];
int in[N];
int tmp;ll ans;
inline void add(int x,int y,int l,int r,int zo){
	r-=l;
	in[x]-=l;
	in[y]+=l;
	if(!r)return;
	u[++t]=x;v[t]=y;c[t]=r;co[t]=zo;nxt[t]=g[x];g[x]=t;
	u[++t]=y;v[t]=x;c[t]=0;co[t]=-zo;nxt[t]=g[y];g[y]=t;
}
bool spfa(){
	int x,i;
	for(i=1;i<=TT;i++)d[i]=inf,vis[i]=0;
	d[SS]=0;
	vis[SS]=1;
	l=r=M>>1;
	q[l]=SS;
	while(l<=r){
		int x=q[l++];
		if(x==TT)continue;
		for(i=g[x];i;i=nxt[i])if(c[i]&&co[i]+d[x]<d[v[i]]){
			d[v[i]]=co[i]+d[x];f[v[i]]=i;
			if(!vis[v[i]]){
				vis[v[i]]=1;
				if(d[v[i]]<d[q[l]])q[--l]=v[i];else q[++r]=v[i];
			}
		}
		vis[x]=0;
	}
	return d[TT]<inf;
}
int main(){
	freopen("threads.in","r",stdin);
	scanf("%d",&Case);
	while(Case--){
		scanf("%d%d",&n,&m);
		sum=0;
		for(i=1;i<=m;i++)scanf("%d",&a[i]),sum+=a[i];
		for(i=1;i<=n;i++)scanf("%d",&b[i]);
		
		S=n*2+m+1;
		T=S+1;
		SS=T+1;
		TT=SS+1;
		for(t=i=1;i<=TT;i++)g[i]=in[i]=0;
		add(T,S,0,N,0);
		for(i=1;i<=n;i++){
			add(i,i+n,1,1,0);
			add(S,i,0,1,0);
			for(j=1;j<=m;j++)if(b[i]<=a[j])add(i+n,j+n*2,0,1,-b[i]);
			for(j=i+1;j<=n;j++)if(b[i]<b[j])add(i+n,j,0,1,0);
		}
		for(i=1;i<=m;i++)add(i+n*2,T,0,1,0);
		for(i=1;i<=TT;i++){
			if(in[i]>0)add(SS,i,0,in[i],0);
			if(in[i]<0)add(i,TT,0,-in[i],0);
		}
		ans=0;
		while(spfa()){
			for(tmp=N,i=TT;i!=SS;i=u[f[i]])if(tmp>c[f[i]])tmp=c[f[i]];
			for(ans+=d[i=TT]*tmp;i!=SS;i=u[f[i]])c[f[i]]-=tmp,c[f[i]^1]+=tmp;
		}
		ans*=-1;
		printf("%lld ",sum-ans);
		
		S=n*2+m+1;
		T=S+1;
		SS=T+1;
		TT=SS+1;
		for(t=i=1;i<=TT;i++)g[i]=in[i]=0;
		add(T,S,0,N,0);
		for(i=1;i<=n;i++){
			add(i,i+n,1,1,0);
			add(S,i,0,1,0);
			for(j=1;j<=m;j++)if(b[i]<=a[j])add(i+n,j+n*2,0,1,b[i]);
			for(j=i+1;j<=n;j++)if(b[i]<b[j])add(i+n,j,0,1,0);
		}
		for(i=1;i<=m;i++)add(i+n*2,T,0,1,0);
		for(i=1;i<=TT;i++){
			if(in[i]>0)add(SS,i,0,in[i],0);
			if(in[i]<0)add(i,TT,0,-in[i],0);
		}
		ans=0;
		while(spfa()){
			for(tmp=N,i=TT;i!=SS;i=u[f[i]])if(tmp>c[f[i]])tmp=c[f[i]];
			for(ans+=d[i=TT]*tmp;i!=SS;i=u[f[i]])c[f[i]]-=tmp,c[f[i]^1]+=tmp;
		}
		printf("%lld\n",sum-ans);
	}
}
/*
3
6 3
8 12 7
5
3
7
3
4
9
3 3
1000000000 999999999 999999998
1
10
1000
5 3
9 5 8
9
3
6
5
8


*/

  

L. Knights

设$f[i][a][b][c][d]$表示从上到下考虑前$i$行,最后$4$行的骑士跳跃方向分别为$a,b,c,d$的方案数。

需要根据可行性将$a$压缩到$3$,$b,c$压缩到$5$,$d$压缩到$7$才能通过。

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=550,P=1000000007;
int cnt,id[3][5][5][7],p[N][4],g[N][9];
int Case,n,m,w,i,j,k,a[N],f[N][N],ans;
char ch[N];
int v[N][N];
int dx[9]={0,2,2,1,1,-1,-1,-2,-2};
int dy[9]={0,-1,1,-2,2,-2,2,-1,1};
inline void up(int&a,int b){
	a=a+b<P?a+b:a+b-P;
}
void init(){
	for(int A=0;A<3;A++)for(int B=0;B<5;B++)for(int C=0;C<5;C++)for(int D=0;D<7;D++){
		int now=cnt++;
		id[A][B][C][D]=now;
		p[now][0]=A;
		p[now][1]=B;
		p[now][2]=C;
		p[now][3]=D;
	}
	for(int A=0;A<3;A++)for(int B=0;B<5;B++)for(int C=0;C<5;C++)for(int D=0;D<7;D++){
		int now=id[A][B][C][D];
		for(int i=1;i<9;i++){
			int nA=B,nB=C,nC=D,nD=i;
			if(nA>=3)nA=0;
			if(nB>=5)nB=0;
			if(nC>=5)nC=0;
			if(nD>=7)nD=0;
			g[now][i]=id[nA][nB][nC][nD];
		}
	}
}
int main(){
	freopen("knights.in","r",stdin);
	init();
	scanf("%d",&Case);
	while(Case--){
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;i++){
			scanf("%s",ch+1);
			for(j=1;j<=m;j++){
				if(ch[j]=='*')a[i]=j;
				v[i][j]=ch[j]!='.';
			}
		}
		for(i=0;i<=n;i++)for(j=0;j<cnt;j++)f[i][j]=0;
		f[0][0]=1;
		for(i=0;i<n;i++)for(j=0;j<cnt;j++)if(f[i][j]){
			w=f[i][j];
			int A=p[j][0],B=p[j][1],C=p[j][2],D=p[j][3];
			if(A)v[i-3+dx[A]][a[i-3]+dy[A]]++;
			if(B)v[i-2+dx[B]][a[i-2]+dy[B]]++;
			if(C)v[i-1+dx[C]][a[i-1]+dy[C]]++;
			if(D)v[i+dx[D]][a[i]+dy[D]]++;
			for(k=1;k<9;k++){
				int x=i+1+dx[k],y=a[i+1]+dy[k];
				if(x>=1&&x<=n&&y>=1&&y<=m&&!v[x][y])
					up(f[i+1][g[j][k]],w);
			}
			if(A)v[i-3+dx[A]][a[i-3]+dy[A]]--;
			if(B)v[i-2+dx[B]][a[i-2]+dy[B]]--;
			if(C)v[i-1+dx[C]][a[i-1]+dy[C]]--;
			if(D)v[i+dx[D]][a[i]+dy[D]]--;
		}
		ans=0;
		for(j=0;j<cnt;j++)up(ans,f[n][j]);
		printf("%d\n",ans);
	}
}

  

M. Winning Cells

问题等价于两个$K-Nim$游戏,那么只要$A\bmod (K+1)\neq B\bmod (K+1)$则先手必胜。

反过来考虑计算必败态的个数,那么考虑$1$到$N$每个数模$K+1$的值,可以将这些数分成贡献不同的两组分别计算。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100010;
int T,n,k;long long ans;
inline int fun(int a,int b){return a|b;}
int main(){
	freopen("chess.in","r",stdin);
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&k);k++;
		int m=n/k*k;
		int base=n/k;
		//[0..k-1] base
		//[1..n%k] base+1
		ans=1LL*n*n;
		int A=n%k,B=k-A;
		ans-=1LL*base*base*B;
		base++;
		ans-=1LL*base*base*A;
		printf("%lld\n",ans);
	}
}

  

posted @ 2017-11-17 10:46 Claris 阅读(...) 评论(...) 编辑 收藏