cf好了 让我们开始
T1 Two Regular Polygons
判断能不能构造出题中要求的正多边形
关键是n%m==0
Two Regular Polygons
#include<bits/stdc++.h>
using namespace std;
int t;
int n,m;
int main()
{
cin>>t;
for(int i=1;i<=t;i++)
{
cin>>n>>m;
if(n%m==0)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
T2 Bogsort
就是要求:
关键是要排序后再反转,目的是确保答案的唯一性,就是昨天构造的知识点,但是比昨天简单多了qwq
Bogosort
#include<bits/stdc++.h>
using namespace std;
int arr[105];
bool cmp(const int &a,const int &b)
{
return a>b;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&arr[i]);
sort(arr,arr+n,cmp);
for(int i=0;i<n;i++)
{
if(i<n-1)
printf("%d ",arr[i]);
else
printf("%d\n",arr[i]);
}
}
return 0;
}
T3 Adding Powers
对于在第i次操作,可以选择不做也可以选择将pos的位置的数减去k的i次方,问能否变成0.
典型的进制转换,也就是把每个数进行分解,记分解完的第i是x_i,按题所讲就是不能大于1,然后使用记录下k进制下第i位的和,然后判断是否有大于1的
Adding Powers
#include<bits/stdc++.h>
using namespace std;
long long a[35];
int trans[35][105],cnt[35];
int n,k;
void divide(int i,long long x,int m)
{
if(x==0)
return;
trans[i][++cnt[i]]=x%m;
divide(i,x/m,m);
return;
}
bool judge()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=cnt[i];j++)
if(trans[i][j]==1)
for(int k=1;k<=n;k++)
if(k!=i&&trans[k][j]==1)
return false;
return true;
}
int main()
{
int t;
cin>>t;
while(t--)
{
memset(trans, 0, sizeof(trans));
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
cnt[i]=0;
divide(i,a[i],k);
}
bool flag=true;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=cnt[i];j++)
if(trans[i][j]>1)
{
flag=false;
break;
}
}
if(!flag)
{
cout<<"NO"<<endl;
continue;
}
flag=judge();
if(!flag)
cout<<"NO"<<endl;
else
cout<<"YES"<<endl;
}
return 0;
}
T4 Count the Arrays
组合数学
蛮喜欢的一集
关键公式是C(m,n-1)*(n-2)%mod*q_pow(2,n-3)%mod
对,要开快速幂
要不然会被卡
然后数据范围也挺大的,要用longlong
Count the Arrays
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const int N=110;
const int mod=998244353;
LL q_pow(LL a,LL b)
{
if(b<0)
return 0;
LL ans=1;
while(b)
{
if(b&1)
ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
LL C(int n,int m)
{
LL ans1=1;
LL ans2=1;
for(int i=0;i<m;i++)
{
ans1=ans1*(n-i)%mod;
ans2=ans2*(i+1)%mod;
}
ans2=q_pow(ans2,mod-2);
return ans1*ans2%mod;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
printf("%lld\n",C(m,n-1)*(n-2)%mod*q_pow(2,n-3)%mod);
return 0;
}
T5 Array Shrinking
区间DP
还以为是树上DP
唐唐Merlin
但是确实是在DAG上的dp
这点很晚才想到
耽误时间了
就是进行dfs
我们设ans[i][j]是区间上最少会被合并成多少个点,若数据成立,那么ans[i][j]=1,所以方程就是:
ans[i][j]=min{ans[i][k]+ans[k+1][j],k∈[l,r-1]}
但是我没有想到一种特殊情况,所以挂了
后来听讲评时才明白
就是:
ans[i][k]=ans[k+1][j]=1并且f[i][k]=f[k+1][j]就可以合并区间了,也就是ans[i][j]=1,f[i][j]=f[i][k]+1
Array Shrinking
#include<bits/stdc++.h>
using namespace std;
int val[600][600];
int dp[600][600];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>val[i][i];
}
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
dp[i][j]=j-i+1;
}
}
for(int len=1;len<=n-1;len++)
{
for(int i=1;i<=n-len;i++)
{
int j=i+len;
for(int k=i;k<=j-1;k++)
{
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
if((dp[i][k]==1&&dp[k+1][j]==1)&&val[i][k]==val[k+1][j])
{
dp[i][j]=1;
val[i][j]=val[i][k]+1;
}
}
}
}
cout<<dp[1][n]<<endl;
return 0;
}
T6 Attack on the Red Kingdom
博弈论SG函数
来HL之前练少了
不是很熟练~
但是讲评听懂了
不能连续使用的限制固定在每个数自身上,所以可以进行单独游戏SG计算再异或。
考虑单个游戏的SG计算。
对于状态数特别大的SG计算,有两种想法:打表找规律,找循环节。这里显然是后者。因为变化不多,只有3种,所以SG值∈[0,4]。并且每次i→[i−5,i−1]。
单个状态无非是SG[i][j]表示值为i上次攻击为j。然后将SG[i−4,i][0,2]这15个状态塞进map,找到循环节。
最后,对每个数尝试每种攻击之后的状态,判断是不是0(对对面必输态)。
但是我好想想出来了一种新的乱搞,实现了一个博弈论中的状态压缩算法,通过预处理状态数组和权重数组,计算游戏局面的必胜策略数。具体的游戏规则和输入输出格式可能需要根据具体的题目描述来理解。
是吗?
Attack on Red Kingdom
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
typedef double db;
const ll mod=1000000007;
ll powmod(ll a,ll b)
{
ll res=1;a%=mod;
assert(b>=0);
for(;b;b>>=1)
{
if(b&1)
res=res*a%mod;a=a*a%mod;
}
return res;}
ll gcd(ll a,ll b)
{
return b?gcd(b,a%b):a;
}
const int N=301000;
int i,n,p[10],wt[1100],pp;
int dp[1100][10];
ll a[N];
void solve()
{
for(int i=1;i<=500;i++)
{
int z=0;
for(int j=0;j<3;j++)
{
set<int> sg;
rep(k,0,3)
if(j==0||k==0||j!=k)
{
if(i-p[k]>=0)
sg.insert(dp[i-p[k]][k]);
else
sg.insert(0);
}
dp[i][j]=0;
while(sg.count(dp[i][j]))
dp[i][j]+=1;
z=z*4+dp[i][j];
}
wt[i]=z;
}
for(int prd=1;prd<=30;prd++)
{
bool sm=1;
for(int j=50;j<500;j++)
if(j+prd<=500&&wt[j]!=wt[j+prd])
sm=0;
if(sm)
{
pp=prd;
return;
}
}
assert(0);
}
int getsg(ll n,int way)
{
int st=0;
if(n<=0)
return 0;
if(n>500)
{
n-=(n-500)/pp*pp;
while(n>500)
n-=pp;
}
return dp[n][way];
}
int main()
{
for(scanf("%d",&i);i;i--)
{
scanf("%d",&n);
rep(i,0,3)
scanf("%d",p+i);
solve();
int s=0;
rep(i,0,n)
{
scanf("%lld",a+i);
s^=getsg(a[i],0);
}
int ans=0;
rep(i,0,n)
rep(j,0,3)
if((s^getsg(a[i],0)^getsg(a[i]-p[j],j))==0)
ans+=1;
printf("%d\n",ans);
}
return 0;
}
T7 Autocompletion
大概是字典树上跑dp,然后用前缀和维护?
确实如此
但是就要回寝了
所以是照着题解写的
Autocompletion
#include<bits/stdc++.h>
using namespace std;
int n,m,check1,q[1001000];
struct dict1
{
int ch[26],f;
bool fin;
}t[1001000];
stack<pair<int,int> >s;
void gs(int x)
{
if(s.empty()||t[x].f-check1<s.top().second)
s.push(make_pair(x,t[x].f-check1));
check1+=t[x].fin;
for(int i=0;i<26;i++)
{
if(!t[x].ch[i])
continue;
t[t[x].ch[i]].f=t[x].f+1;
if(!s.empty()&&t[t[x].ch[i]].fin)
t[t[x].ch[i]].f=min(t[t[x].ch[i]].f,s.top().second+check1+1);
gs(t[x].ch[i]);
}
if(!s.empty()&&s.top().first==x)
s.pop();
}
char str[2];
int main()
{
scanf("%d",&n);
for(int i=1,x;i<=n;i++)
scanf("%d%s",&x,str),t[x].ch[str[0]-'a']=i;
scanf("%d",&m);
for(int i=1,x;i<=m;i++)
scanf("%d",&q[i]),t[q[i]].fin=true;
gs(0);
for(int i=1;i<=m;i++)
printf("%d ",t[q[i]].f);
return 0;
}